diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f57e6f3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Patches are applied to LF sources on the Linux terminal. +terminal/moonlight/patches/*.patch text eol=lf diff --git a/MEMORY.md b/MEMORY.md index e346314..690ebef 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -2,7 +2,42 @@ 完整对话总结在:`D:\DAIMA\vdi-cloud\PROJECT_CONTEXT.md` +## 2026-11-03 显示器关开修复(优先于下文旧状态) +- 关屏再开后画面变矮:盒子回退1920x1440@75(4:3),Moonlight 16:9视频上下黑边; + MATE monitors.xml仍存旧4:3布局,原启动脚本只设置一次2K。 +- 已部署vdi-display-guard用户服务(active/enabled)和幂等启动恢复,保留297MHz定制2K时序; + 2秒检查、异常稳定2秒后恢复;断开不强设、失败退避、命令超时、单实例锁,正常不反复切换。 + 修正当前GWR/0x2708显示器旧保存布局。仅部署tools/deploy_display_guard.sh,不重启串流。 +- 9个场景测试通过,CTest 3/3;两次主动回退均约14秒恢复,Moonlight PID66385和boot ID不变。 + 用户真实关屏再开确认自动全屏;10:03:19断开、10:03:31正确2K模式,10:04现场2560x1440。 +- 备份~/vdi-terminal/backups/display-guard-20261103-095920/;详情/回退见 + docs/display-resume-fix-20261103.md。此修复仅限当前Xorg单HDMI 2K POC,未做隔夜老化。 + +## 2026-11-03 鼠标/点击跳转优化(优先于下文旧状态) +- Moonlight私有客户端新增两个补丁:鼠标合并不跨点击/键盘/窗口边界,绝对鼠标按钮封住前一位置批次; + 低延迟非pacing队列可丢弃等待帧保留最新帧,wrapper默认VDI_LATEST_FRAME=1。保持单白色Windows光标。 +- 现场32次交替点击:旧客户端30/32正确(第1、13错位),补丁32/32正确;native输入测试1000轮和ASAN通过。 + 配对串流补丁队列2.14ms/呈现6.12ms,原3.39ms/6.84ms;帧率约28.4/28.1,受网络/软件编码波动,不能称零延迟。 +- Windows保留DragFullWindows=1、MouseTrails=0;已在test交互会话将MinAnimate从1改0、菜单400改100ms,读回通过, + 原值备份在test的LocalAppData/VDI;临时执行/测试任务已清理。 +- 用户确认蓝框在盒子登录/选择页:QFrame:hover错误匹配QLabel。移除蓝色悬停框和登录焦点蓝框,限定卡片对象样式, + 局部编译CTest3/3,部署新版kiosk;悬停截图亮蓝像素8302→0。UI/wrapper备份input-rendering-20261103-114542。 + 详情见docs/input-rendering-optimization-20261103.md和terminal/moonlight/README.md。 + ## 2026-11-02 最新实测(优先于下文旧状态) +- 2026-11-02晚:已在用户目录完成MPP+FFmpeg-Rockchip+Moonlight6.2.0私有构建, + 实际2K串流选中h264_rkmpp/drm_prime与EGL/GLES呈现,非仅FFmpeg样本测试。 + 2K20 smooth会话平均呈现18.97FPS、解码10.53ms、呈现2.54ms;2K30 low-latency + 呈现23.46FPS、解码9.11ms、队列2.39ms、呈现5.41ms。服务端仍Code43/libx264。 + 2K30 smooth队列31.95ms,VSync开/帧步调关也有28.93ms,故该盒子选低延迟档。 + 原始日志和限制见 `docs/rendering-execution-20261102.md`。 + 通过 `~/.config/vdi-terminal/render.conf` 显式选择私有wrapper+hardware,系统客户端不覆盖; + Windows桌面票据改30FPS,原20FPS文件备份为desktops.before-rkmpp-20261102.json。 + 构建脚本 `tools/build-rkmpp-client.sh` 修Qt5 optional、EGL探测缓存与旧对象不重编; + kiosk就绪判断兼容RKMPP独立测试解码器日志,CTest 2/2通过。 + 鼠标MouseTrails=0、DragFullWindows=1;现场仍有VDIApplyTransparentCursor刷新参数任务, + 先前“任务已删除”的记录不准确,本轮未改变光标配置;截图单白光标正常。 + 不能把23FPS、硬解接通解释为GPU已恢复、2K60或端到端零延迟。 - 已部署真实2K:盒子HDMI 2560x1440约60Hz、Windows VDD 2560x1440@30、Moonlight 2560x1440@20/30Mbps。 - RK3528旧PHY拒绝EDID的241.70MHz,实测用297MHz固定时钟和扩展消隐成功: `297.00 2560 2608 2640 2720 1440 1443 1448 1820 +HSync -VSync`;盒子重启已验证,link Good。 @@ -90,4 +125,4 @@ - 协议最终选型定版(RDP / Moonlight / 双协议,需实测数据 + 产品方案共同确认) - 监控与运维脚本、OTA、USB 重定向等终端其余能力 -先读 `PROJECT_CONTEXT.md`,再查看 `git status`,不要丢弃未提交改动。 \ No newline at end of file +先读 `PROJECT_CONTEXT.md`,再查看 `git status`,不要丢弃未提交改动。 diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index 78724cf..6d0e8d6 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -258,11 +258,8 @@ Qt QImage/QLabel 显示 重要:当前 POC 已成功显示 Windows 10 的真实远程桌面画面,颜色正确,进程保持运行。这是目前最重要的实测成果。 -详细说明在: - -```text -D:\DAIMA\vdi-cloud\docs\freerdp3-embedding-notes.md -``` +这部分属于早期 FreeRDP POC 历史记录;对应的临时笔记文件已从仓库移除。 +当前终端会话实现和维护说明以 `terminal/kiosk-shell/` 及其 README 为准。 ### 6.5 显示问题 @@ -276,12 +273,15 @@ D:\DAIMA\vdi-cloud\docs\freerdp3-embedding-notes.md 注意:此前对“X11 无 glamor 硬件合成”的判断不准确,Xorg 日志实际显示 `glamor X acceleration enabled on Mali450`;后续不要继续引用这个错误结论。正确表述是:当前路径仍未达到目标流畅度,具体瓶颈需要后续基准测试确认。 -### 6.6 Moonlight 云桌面开机直达(2026-10-28) +### 6.6 Moonlight 云桌面开机直达(2026-10-28,历史记录) + +本节保留当时的联调过程和取舍。2026-11-03 的实际部署状态、显示恢复、输入补丁及 +渲染参数以后续 `MEMORY.md` 和对应日期的 `docs/` 记录为准。 RDP/Moonlight 实测对比见 `docs/rdp-vs-moonlight-comparison-20261028.md`, 落地记录见 `docs/moonlight-kiosk-20261028.md`。 -盒子当前开机默认走 Moonlight 直连云桌面,链路: +当时盒子开机默认走 Moonlight 直连云桌面,链路如下: ```text ~/.config/autostart/vdi-moonlight.desktop diff --git a/README.md b/README.md index c349a2b..1711339 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ vdi-cloud/ ├─ platform/ # Go 云端服务 ├─ console/ # Vue 3 + TypeScript Web 管理控制台 ├─ deploy/ # Docker Compose、Helm、离线部署包 -├─ docs/ # OpenAPI、MQTT、数据库等正式协议文档 +├─ docs/ # 方案文档、实施记录、测试证据和运维资料 └─ tools/ # 烧录、构建、压测和 CI 工具 ``` diff --git a/docs/.gitkeep b/docs/.gitkeep deleted file mode 100644 index 728945f..0000000 --- a/docs/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# Keep the monorepo directory structure in Git. diff --git a/docs/display-resume-fix-20261103.md b/docs/display-resume-fix-20261103.md new file mode 100644 index 0000000..ec82690 --- /dev/null +++ b/docs/display-resume-fix-20261103.md @@ -0,0 +1,70 @@ +# 2026-11-03 显示器关屏再开后云桌面变矮:修复与验收 + +已在 `armbian@192.168.1.188` 部署。用户现场关闭显示器、等待后重新打开,确认“已自动恢复全屏”。修复过程没有重启盒子,也没有重启或重连 Moonlight。 + +## 原因与证据 + +- 异常时盒子 HDMI 输出为 **1920×1440@75Hz(4:3)**,Windows/Moonlight 视频为 **2560×1440(16:9)**。 +- Moonlight 全屏窗口跟随盒子变为 1920×1440,视频保持宽高比,因此视频内容为 1920×1080,上下各有约 180 像素黑边,表现为“宽度一样,高度矮了一点”。异常截图见 `evidence/display-resume-20261103/before.png`。 +- MATE 的 `~/.config/monitors.xml` 还保存着 HDMI-1 的 1920×1440@75Hz 配置;原启动脚本只在启动时设置一次 2K,没有处理后续热插拔和模式回退。 +- 显示器 EDID 的 241.70MHz 时序仍不适用于此 RK3528 vendor PHY;沿用此前已验证的 297MHz 定制 2K 时序,不切回该原厂时序。 + +## 修改 + +1. 新增 `terminal/kiosk-shell/scripts/vdi-display-guard`:每 2 秒检查 XRandR 状态,模式异常持续至少 2 秒后恢复 `2560x1440_60_fixed297`。模式已存在时不重复创建,热插拔后丢失关联时重新关联。 +2. 健康状态只查询,不反复设置分辨率;显示器断开或 X11 尚不可用时等待,不强设;设置失败按 2/4/8/16/32/60 秒退避,并在设置后重新读取验证。XRandR 每条命令有 10 秒超时。 +3. 新增用户级 `vdi-display-guard.service`,已启用 `default.target` 自启、异常退出重启;使用文件锁防止启动恢复与常驻守护竞争。服务独立受 systemd 管理,不挂无主后台循环。 +4. `vdi-kiosk-start` 使用相同恢复程序做启动检查,再确保服务运行;保留此前渲染配置解析及单一 kiosk 启动链。 +5. 将本机已知显示器(HDMI-1,GWR/0x2708)的旧保存布局改为 2560×1440@60,其余布局不改。部署前保存原文件。 +6. 新增只部署显示修复的 `tools/deploy_display_guard.sh`;完整 `deploy_kiosk.sh` 也接入该步骤,避免下次部署覆盖修复。此次只执行前者,未执行会停止串流的完整部署。 + +时序仍为: + +```text +297.00 2560 2608 2640 2720 1440 1443 1448 1820 +HSync -VSync +``` + +本次仅调整盒子本地显示,不修改 Windows、Sunshine、GPU、远程光标或已有硬解参数。 + +## 验收 + +| 项目 | 结果 | +| --- | --- | +| 自动化显示恢复场景 | 9/9 通过:正常不写、断开/重连、首次创建、重新关联、晚到覆盖、失败退避、设置结果校验、X11 暂不可用等 | +| 盒子 CTest | 3/3 通过:display-guard、moonlight-session、desktop-list-page | +| 连续两次主动回退 1920×1440@75 | 均自动恢复 2560×1440,全屏窗口恢复 2560×1440,单次从发起切换到观测恢复约 14 秒(含两次驱动切换耗时) | +| 真实显示器电源循环 | 用户确认自动恢复全屏;日志在 10:03:19 检测断开,10:03:31 检测到正确 2K 模式,无需额外模式设置 | +| 会话连续性 | Moonlight PID 始终为 66385,窗口始终为 44040203;模拟测试前后 boot ID 不变;服务未发生异常重启 | +| 最终现场 | 2026-11-03 10:04,HDMI 2560×1440 约 60Hz,Moonlight 窗口 2560×1440,守护 active/enabled | + +原始测试输出:`evidence/display-resume-20261103/recovery-test.txt`。最终模式、进程及包含真实关屏过程的日志:`evidence/display-resume-20261103/final-state.txt`。真实开屏后的截图:`evidence/display-resume-20261103/after-power-cycle.png`。 + +这是当前这一台盒子与显示器的现场验收,未执行隔夜或多型号显示器测试。守护固定针对当前单 HDMI、2K、Xorg POC;以后换不同显示器或迁移到 Weston/Wayland 时需改为对应的输出配置机制。当前修复不代表服务端 Code43、帧率或鼠标动态残影问题已解决。 + +## 运维 + +Windows 开发机在 Git Bash 中只部署此修复: + +```bash +bash tools/deploy_display_guard.sh +``` + +盒子上查看状态与日志(当前环境 `journalctl --user` 看不到用户服务日志,因此使用系统日志的用户单元过滤): + +```bash +systemctl --user status vdi-display-guard.service +sudo -n journalctl _SYSTEMD_USER_UNIT=vdi-display-guard.service -n 50 --no-pager +DISPLAY=:0 XAUTHORITY=/home/armbian/.Xauthority xrandr --query +``` + +本次部署备份目录为 `/home/armbian/vdi-terminal/backups/display-guard-20261103-095920/`,包含原 `vdi-kiosk-start` 和原 `monitors.xml`。必要时可回退到原行为;这也会恢复旧的 4:3 保存布局和“仅启动时修复”的限制: + +```bash +systemctl --user disable --now vdi-display-guard.service +cp -p ~/vdi-terminal/backups/display-guard-20261103-095920/vdi-kiosk-start ~/vdi-terminal/bin/vdi-kiosk-start +cp -p ~/vdi-terminal/backups/display-guard-20261103-095920/monitors.xml ~/.config/monitors.xml +rm -- ~/.config/systemd/user/vdi-display-guard.service +systemctl --user daemon-reload +``` + +日常操作:盒子保持开机,显示器可正常关开。若 HDMI 模式确实回退,给驱动和恢复程序约 15 秒完成切换,无需为此重启盒子。 diff --git a/docs/evidence/display-resume-20261103/after-power-cycle.png b/docs/evidence/display-resume-20261103/after-power-cycle.png new file mode 100644 index 0000000..b82d8e8 Binary files /dev/null and b/docs/evidence/display-resume-20261103/after-power-cycle.png differ diff --git a/docs/evidence/display-resume-20261103/before.png b/docs/evidence/display-resume-20261103/before.png new file mode 100644 index 0000000..7286101 Binary files /dev/null and b/docs/evidence/display-resume-20261103/before.png differ diff --git a/docs/evidence/display-resume-20261103/final-state.txt b/docs/evidence/display-resume-20261103/final-state.txt new file mode 100644 index 0000000..12fccaa --- /dev/null +++ b/docs/evidence/display-resume-20261103/final-state.txt @@ -0,0 +1,43 @@ +2026-11-03T10:04:16+08:00 +Screen 0: minimum 320 x 200, current 2560 x 1440, maximum 16384 x 16384 +HDMI-1 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 597mm x 336mm + 1920x1440 75.00 + 2048x1152 60.00 + 1920x1080 100.00 60.00 60.00 50.00 59.94 + 1920x1080i 60.00 50.00 59.94 + 1600x1200 60.00 + 1680x1050 59.88 + 1600x900 60.00 + 1280x1024 60.02 + 1440x900 59.90 + 1280x960 85.00 60.00 + 1280x800 59.91 + 1152x864 75.00 + 1280x720 100.00 60.00 50.00 59.94 + 1024x768 60.00 + 800x600 60.32 + 720x576 50.00 + 720x480 59.94 + 2560x1440_60_fixed297 60.00* +WINDOW=44040203 +X=0 +Y=0 +WIDTH=2560 +HEIGHT=1440 +SCREEN=0 +66385 /home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.70 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --keep-awake --no-vsync --no-frame-pacing --audio-on-host +MainPID=76385 +NRestarts=0 +CPUUsageNSec=2960579000 +ActiveState=active +UnitFileState=enabled +11月 03 09:59:22 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 09:59:22 mode drift: 1920x1440 1920x1440+0+0 +11月 03 09:59:30 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 09:59:30 restored: 2560x1440_60_fixed297 (stream process untouched) +11月 03 09:59:33 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 09:59:33 healthy: 2560x1440_60_fixed297 +11月 03 10:00:26 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:26 mode drift: 1920x1440 1920x1440+0+0 +11月 03 10:00:34 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:34 restored: 2560x1440_60_fixed297 (stream process untouched) +11月 03 10:00:41 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:41 mode drift: 1920x1440 1920x1440+0+0 +11月 03 10:00:49 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:49 restored: 2560x1440_60_fixed297 (stream process untouched) +11月 03 10:00:51 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:51 healthy: 2560x1440_60_fixed297 +11月 03 10:03:19 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:03:19 HDMI-1 disconnected; no mode changes +11月 03 10:03:31 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:03:31 healthy: 2560x1440_60_fixed297 diff --git a/docs/evidence/display-resume-20261103/initial-guard-journal.txt b/docs/evidence/display-resume-20261103/initial-guard-journal.txt new file mode 100644 index 0000000..1ad538e --- /dev/null +++ b/docs/evidence/display-resume-20261103/initial-guard-journal.txt @@ -0,0 +1,10 @@ +--query seconds= 0.109 +--current seconds= 0.015 +11月 03 09:59:22 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 09:59:22 mode drift: 1920x1440 1920x1440+0+0 +11月 03 09:59:30 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 09:59:30 restored: 2560x1440_60_fixed297 (stream process untouched) +11月 03 09:59:33 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 09:59:33 healthy: 2560x1440_60_fixed297 +11月 03 10:00:26 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:26 mode drift: 1920x1440 1920x1440+0+0 +11月 03 10:00:34 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:34 restored: 2560x1440_60_fixed297 (stream process untouched) +11月 03 10:00:41 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:41 mode drift: 1920x1440 1920x1440+0+0 +11月 03 10:00:49 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:49 restored: 2560x1440_60_fixed297 (stream process untouched) +11月 03 10:00:51 rk3528pro-ddr4 vdi-display-guard[76385]: 2026-11-03 10:00:51 healthy: 2560x1440_60_fixed297 diff --git a/docs/evidence/display-resume-20261103/recovery-test.txt b/docs/evidence/display-resume-20261103/recovery-test.txt new file mode 100644 index 0000000..a59f5b5 --- /dev/null +++ b/docs/evidence/display-resume-20261103/recovery-test.txt @@ -0,0 +1,25 @@ +before_stream_pid=66385 boot_id=89a44e11-5cfd-46bb-ae72-3232edc4ea62 +enabled +active +2026-11-03 10:00:20 display guard already running +cycle=1 drift_at=2026-11-03T10:00:20+08:00 +cycle=1 recovered_at=2026-11-03T10:00:34+08:00 +WINDOW=44040203 +X=0 +Y=0 +WIDTH=2560 +HEIGHT=1440 +SCREEN=0 +cycle=2 drift_at=2026-11-03T10:00:35+08:00 +cycle=2 recovered_at=2026-11-03T10:00:49+08:00 +WINDOW=44040203 +X=0 +Y=0 +WIDTH=2560 +HEIGHT=1440 +SCREEN=0 +PASS: both recoveries kept stream PID and boot ID +MainPID=76385 +NRestarts=0 +CPUUsageNSec=1092599000 +-- No entries -- diff --git a/docs/evidence/display-resume-20261103/restored.png b/docs/evidence/display-resume-20261103/restored.png new file mode 100644 index 0000000..9cf42e7 Binary files /dev/null and b/docs/evidence/display-resume-20261103/restored.png differ diff --git a/docs/evidence/input-latency-20261103/baseline-clicks.txt b/docs/evidence/input-latency-20261103/baseline-clicks.txt new file mode 100644 index 0000000..2de51f5 --- /dev/null +++ b/docs/evidence/input-latency-20261103/baseline-clicks.txt @@ -0,0 +1,71 @@ +width=2560 height=1440 paints=19411 elapsed_ms=536551 clicks=32 + +ms,x,y,target + +536057,1920,600,R + +536060,1920,600,R + +536063,639,600,L + +536070,1920,600,R + +536071,639,600,L + +536072,1920,600,R + +536077,639,600,L + +536078,1920,600,R + +536086,639,600,L + +536148,1920,600,R + +536151,639,600,L + +536152,1920,600,R + +536153,1920,600,R + +536154,1920,600,R + +536155,639,600,L + +536156,1920,600,R + +536157,639,600,L + +536158,1920,600,R + +536159,639,600,L + +536160,1920,600,R + +536161,639,600,L + +536162,1920,600,R + +536163,639,600,L + +536164,1920,600,R + +536165,639,600,L + +536166,1920,600,R + +536167,639,600,L + +536168,1920,600,R + +536169,639,600,L + +536170,1920,600,R + +536171,639,600,L + +536172,1920,600,R + +{"LastTaskResult":267009} + + diff --git a/docs/evidence/input-latency-20261103/baseline-stats.txt b/docs/evidence/input-latency-20261103/baseline-stats.txt new file mode 100644 index 0000000..a4eefc0 --- /dev/null +++ b/docs/evidence/input-latency-20261103/baseline-stats.txt @@ -0,0 +1,37 @@ +ELAPSED COMMAND + 93 /home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.70 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --keep-awake --no-vsync --no-frame-pacing --audio-on-host --performance-overlay +------------------ +Incoming frame rate from network: 27.45 FPS +Decoding frame rate: 27.45 FPS +Rendering frame rate: 27.14 FPS +Host processing latency min/max/average: 27.3/87.5/48.5 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.52% +Average network latency: 6 ms (variance: 5 ms) +Average decoding time: 9.84 ms +Average frame queue delay: 4.50 ms +Average rendering time (including monitor V-sync latency): 6.89 ms +00:01:34 - SDL Info (0): Stopping input stream... +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Stopping audio stream... +00:01:34 - SDL Info (0): No audio traffic was ever received from the host! +00:01:34 - SDL Info (0): ENet wait interrupted +00:01:34 - SDL Info (0): Control stream connection failed: 4 +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Stopping video stream... +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Stopping control stream... +00:01:34 - SDL Info (0): ENet peer acknowledged disconnection +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Cleaning up input stream... +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Cleaning up video stream... +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Cleaning up control stream... +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Cleaning up audio stream... +00:01:34 - SDL Info (0): done +00:01:34 - SDL Info (0): Cleaning up platform... +00:01:34 - SDL Info (0): done +00:01:34 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:01:34 - SDL Info (0): Loaded 436 new gamepad mappings diff --git a/docs/evidence/input-latency-20261103/baseline.png b/docs/evidence/input-latency-20261103/baseline.png new file mode 100644 index 0000000..eb0c1e2 Binary files /dev/null and b/docs/evidence/input-latency-20261103/baseline.png differ diff --git a/docs/evidence/input-latency-20261103/blue-hover-after.png b/docs/evidence/input-latency-20261103/blue-hover-after.png new file mode 100644 index 0000000..2abcfd8 Binary files /dev/null and b/docs/evidence/input-latency-20261103/blue-hover-after.png differ diff --git a/docs/evidence/input-latency-20261103/blue-hover-before.png b/docs/evidence/input-latency-20261103/blue-hover-before.png new file mode 100644 index 0000000..c3527ee Binary files /dev/null and b/docs/evidence/input-latency-20261103/blue-hover-before.png differ diff --git a/docs/evidence/input-latency-20261103/click-comparison.json b/docs/evidence/input-latency-20261103/click-comparison.json new file mode 100644 index 0000000..523f477 --- /dev/null +++ b/docs/evidence/input-latency-20261103/click-comparison.json @@ -0,0 +1,17 @@ +{ + "baseline": { + "received": 32, + "expected": 32, + "wrong_positions": [ + 1, + 13 + ], + "correct": 30 + }, + "patched": { + "received": 32, + "expected": 32, + "wrong_positions": [], + "correct": 32 + } +} diff --git a/docs/evidence/input-latency-20261103/current-screen.png b/docs/evidence/input-latency-20261103/current-screen.png new file mode 100644 index 0000000..326f0c2 Binary files /dev/null and b/docs/evidence/input-latency-20261103/current-screen.png differ diff --git a/docs/evidence/input-latency-20261103/deployed-final.txt b/docs/evidence/input-latency-20261103/deployed-final.txt new file mode 100644 index 0000000..b2bf85d --- /dev/null +++ b/docs/evidence/input-latency-20261103/deployed-final.txt @@ -0,0 +1,15 @@ +101612 /home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.70 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --keep-awake --no-vsync --no-frame-pacing --audio-on-host +Renderer 'SDL' with 'DRM' backend chosen +00:00:09 - SDL Info (0): VDI latest-frame queue: enabled +00:00:09 - SDL Info (0): Renderer 'EGL/GLES' with 'DRM' backend chosen +vdi_connection attempt=1 desktop=desk-win10-01 phase=ticket result=ready phase_ms=20 total_ms=20 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=process_start result=started phase_ms=0 total_ms=21 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=process_start result=ready phase_ms=103 total_ms=125 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=stream_ready result=ready phase_ms=10440 total_ms=10566 error= +active +WINDOW=44040203 +X=0 +Y=0 +WIDTH=2560 +HEIGHT=1440 +SCREEN=0 diff --git a/docs/evidence/input-latency-20261103/final-terminal-state.txt b/docs/evidence/input-latency-20261103/final-terminal-state.txt new file mode 100644 index 0000000..3f129b8 --- /dev/null +++ b/docs/evidence/input-latency-20261103/final-terminal-state.txt @@ -0,0 +1,3 @@ +Screen 0: minimum 320 x 200, current 2560 x 1440, maximum 16384 x 16384 + 2560x1440_60_fixed297 60.00* +active diff --git a/docs/evidence/input-latency-20261103/paired-retest.txt b/docs/evidence/input-latency-20261103/paired-retest.txt new file mode 100644 index 0000000..0d64715 --- /dev/null +++ b/docs/evidence/input-latency-20261103/paired-retest.txt @@ -0,0 +1,30 @@ +baseline2 ready_at=2026-11-03T10:52:11+08:00 pid=95753 +Global video stats +------------------ +Incoming frame rate from network: 28.93 FPS +Decoding frame rate: 28.93 FPS +Rendering frame rate: 28.48 FPS +Host processing latency min/max/average: 27.9/78.7/42.6 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.54% +Average network latency: 9 ms (variance: 4 ms) +Average decoding time: 10.49 ms +Average frame queue delay: 3.39 ms +Average rendering time (including monitor V-sync latency): 6.84 ms +00:00:54 - SDL Info (0): Stopping input stream... +baseline2 complete_at=2026-11-03T10:52:58+08:00 +patched2 ready_at=2026-11-03T10:53:07+08:00 pid=95869 +Global video stats +------------------ +Incoming frame rate from network: 28.40 FPS +Decoding frame rate: 28.40 FPS +Rendering frame rate: 28.09 FPS +Host processing latency min/max/average: 31.2/96.2/42.3 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 1.10% +Average network latency: 4 ms (variance: 4 ms) +Average decoding time: 7.96 ms +Average frame queue delay: 2.14 ms +Average rendering time (including monitor V-sync latency): 6.12 ms +00:00:54 - SDL Info (0): Stopping input stream... +patched2 complete_at=2026-11-03T10:53:53+08:00 diff --git a/docs/evidence/input-latency-20261103/patched-clicks.txt b/docs/evidence/input-latency-20261103/patched-clicks.txt new file mode 100644 index 0000000..2a24159 --- /dev/null +++ b/docs/evidence/input-latency-20261103/patched-clicks.txt @@ -0,0 +1,71 @@ +width=2560 height=1440 paints=1532 elapsed_ms=45518 clicks=32 + +ms,x,y,target + +44625,639,600,L + +44625,1920,600,R + +44627,639,600,L + +44628,1920,600,R + +44629,639,600,L + +44630,1920,600,R + +44651,639,600,L + +44692,1920,600,R + +44693,639,600,L + +44694,1920,600,R + +44695,639,600,L + +44696,1920,600,R + +44697,639,600,L + +44697,1920,600,R + +44698,639,600,L + +44699,1920,600,R + +44703,639,600,L + +44713,1920,600,R + +44716,639,600,L + +44717,1920,600,R + +44718,639,600,L + +44718,1920,600,R + +44719,639,600,L + +44720,1920,600,R + +44721,639,600,L + +44722,1920,600,R + +44722,639,600,L + +44723,1920,600,R + +44746,639,600,L + +44747,1920,600,R + +44748,639,600,L + +44749,1920,600,R + +{"LastTaskResult":267009} + + diff --git a/docs/evidence/input-latency-20261103/patched-stats.txt b/docs/evidence/input-latency-20261103/patched-stats.txt new file mode 100644 index 0000000..ab5df16 --- /dev/null +++ b/docs/evidence/input-latency-20261103/patched-stats.txt @@ -0,0 +1,37 @@ +ELAPSED COMMAND + 147 /home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight stream 192.168.1.70 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --keep-awake --no-vsync --no-frame-pacing --audio-on-host --performance-overlay +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.81% +Average network latency: 15 ms (variance: 14 ms) +Average decoding time: 8.30 ms +Average frame queue delay: 2.72 ms +Average rendering time (including monitor V-sync latency): 6.62 ms +00:02:05 - SDL Info (0): Stopping input stream... +00:02:05 - Qt Critical: 会话已终止 + +错误代码: -1 +00:02:05 - SDL Info (0): done +00:02:05 - Qt Warning: stream 1 finished with error: "Connection closed" +00:02:05 - SDL Info (0): Stopping audio stream... +00:02:05 - SDL Info (0): No audio traffic was ever received from the host! +00:02:05 - SDL Info (0): done +00:02:05 - SDL Info (0): Stopping video stream... +00:02:05 - SDL Info (0): done +00:02:05 - SDL Info (0): Stopping control stream... +00:02:05 - SDL Info (0): ENet peer is already disconnected +00:02:05 - SDL Info (0): done +00:02:05 - SDL Info (0): Cleaning up input stream... +00:02:05 - SDL Info (0): done +00:02:05 - SDL Info (0): Cleaning up video stream... +00:02:05 - SDL Info (0): done +00:02:05 - SDL Info (0): Cleaning up control stream... +00:02:05 - SDL Info (0): done +00:02:05 - SDL Info (0): Cleaning up audio stream... +00:02:05 - SDL Info (0): done +00:02:05 - SDL Info (0): Cleaning up platform... +00:02:05 - SDL Info (0): done +00:02:05 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:02:05 - SDL Info (0): Loaded 436 new gamepad mappings +00:02:05 - Qt Warning: Failed to download latest compatibility data: QNetworkReply::RemoteHostClosedError +00:02:13 - Qt Info: "DESKTOP-Q072ICM" is now offline +00:02:22 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" diff --git a/docs/evidence/input-latency-20261103/patched2-bench-status.txt b/docs/evidence/input-latency-20261103/patched2-bench-status.txt new file mode 100644 index 0000000..2c30877 --- /dev/null +++ b/docs/evidence/input-latency-20261103/patched2-bench-status.txt @@ -0,0 +1,7 @@ +width=2560 height=1440 paints=19799 elapsed_ms=599310 clicks=0 + +ms,x,y,target + +{"LastTaskResult":267014} + + diff --git a/docs/evidence/input-latency-20261103/wifi-disconnect.txt b/docs/evidence/input-latency-20261103/wifi-disconnect.txt new file mode 100644 index 0000000..2866a3d --- /dev/null +++ b/docs/evidence/input-latency-20261103/wifi-disconnect.txt @@ -0,0 +1,28 @@ +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.6211] device (wlan0): supplicant interface state: completed -> disconnected +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.6213] device (p2p-dev-wlan0): supplicant management interface state: completed -> disconnected +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.7019] device (wlan0): supplicant interface state: disconnected -> scanning +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.7030] device (p2p-dev-wlan0): supplicant management interface state: disconnected -> scanning +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.7412] device (wlan0): supplicant interface state: scanning -> associating +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.7415] device (p2p-dev-wlan0): supplicant management interface state: scanning -> associating +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.9321] device (wlan0): supplicant interface state: associating -> disconnected +11月 03 10:48:18 rk3528pro-ddr4 NetworkManager[883]: [1793674098.9324] device (p2p-dev-wlan0): supplicant management interface state: associating -> disconnected +11月 03 10:48:19 rk3528pro-ddr4 NetworkManager[883]: [1793674099.4318] device (wlan0): supplicant interface state: disconnected -> scanning +11月 03 10:48:19 rk3528pro-ddr4 NetworkManager[883]: [1793674099.4320] device (p2p-dev-wlan0): supplicant management interface state: disconnected -> scanning +11月 03 10:48:20 rk3528pro-ddr4 NetworkManager[883]: [1793674100.8003] device (wlan0): supplicant interface state: scanning -> associating +11月 03 10:48:20 rk3528pro-ddr4 NetworkManager[883]: [1793674100.8005] device (p2p-dev-wlan0): supplicant management interface state: scanning -> associating +11月 03 10:48:20 rk3528pro-ddr4 NetworkManager[883]: [1793674100.9901] device (wlan0): supplicant interface state: associating -> disconnected +11月 03 10:48:20 rk3528pro-ddr4 NetworkManager[883]: [1793674100.9903] device (p2p-dev-wlan0): supplicant management interface state: associating -> disconnected +11月 03 10:48:21 rk3528pro-ddr4 NetworkManager[883]: [1793674101.9908] device (wlan0): supplicant interface state: disconnected -> scanning +11月 03 10:48:21 rk3528pro-ddr4 NetworkManager[883]: [1793674101.9910] device (p2p-dev-wlan0): supplicant management interface state: disconnected -> scanning +11月 03 10:48:22 rk3528pro-ddr4 NetworkManager[883]: [1793674102.0281] device (wlan0): supplicant interface state: scanning -> associating +11月 03 10:48:22 rk3528pro-ddr4 NetworkManager[883]: [1793674102.0283] device (p2p-dev-wlan0): supplicant management interface state: scanning -> associating +11月 03 10:48:22 rk3528pro-ddr4 NetworkManager[883]: [1793674102.2189] device (wlan0): supplicant interface state: associating -> disconnected +11月 03 10:48:22 rk3528pro-ddr4 NetworkManager[883]: [1793674102.2192] device (p2p-dev-wlan0): supplicant management interface state: associating -> disconnected +11月 03 10:48:27 rk3528pro-ddr4 NetworkManager[883]: [1793674107.2236] device (wlan0): supplicant interface state: disconnected -> scanning +11月 03 10:48:27 rk3528pro-ddr4 NetworkManager[883]: [1793674107.2239] device (p2p-dev-wlan0): supplicant management interface state: disconnected -> scanning +11月 03 10:48:31 rk3528pro-ddr4 NetworkManager[883]: [1793674111.5980] device (wlan0): supplicant interface state: scanning -> associating +11月 03 10:48:31 rk3528pro-ddr4 NetworkManager[883]: [1793674111.5983] device (p2p-dev-wlan0): supplicant management interface state: scanning -> associating +11月 03 10:48:31 rk3528pro-ddr4 NetworkManager[883]: [1793674111.8979] device (wlan0): supplicant interface state: associating -> associated +11月 03 10:48:31 rk3528pro-ddr4 NetworkManager[883]: [1793674111.8981] device (p2p-dev-wlan0): supplicant management interface state: associating -> associated +11月 03 10:48:31 rk3528pro-ddr4 NetworkManager[883]: [1793674111.9156] device (wlan0): supplicant interface state: associated -> completed +11月 03 10:48:31 rk3528pro-ddr4 NetworkManager[883]: [1793674111.9161] device (p2p-dev-wlan0): supplicant management interface state: associated -> completed diff --git a/docs/evidence/input-latency-20261103/windows-visual-settings.txt b/docs/evidence/input-latency-20261103/windows-visual-settings.txt new file mode 100644 index 0000000..e7e8304 --- /dev/null +++ b/docs/evidence/input-latency-20261103/windows-visual-settings.txt @@ -0,0 +1,30 @@ +{"LastTaskResult":0} + +{ + + "backup": "C:\\Users\\test\\AppData\\Local\\VDI\\desktop-responsiveness-before-20261103.json", + + "before": { + + "animation": 1, + + "menuDelay": 400 + + }, + + "restored": false, + + "after": { + + "animation": 0, + + "menuDelay": 100 + + } + +} + + + + + diff --git a/docs/evidence/moonlight-20261102/decode-drm-prime.txt b/docs/evidence/moonlight-20261102/decode-drm-prime.txt new file mode 100644 index 0000000..2e0f17f --- /dev/null +++ b/docs/evidence/moonlight-20261102/decode-drm-prime.txt @@ -0,0 +1,41 @@ +[h264 @ 0xaaab09e425e0] Reinit context to 2560x1440, pix_fmt: yuv420p +Input #0, h264, from '/home/armbian/vdi-build/rkmpp-experiment/test/1440p.h264': + Duration: N/A, bitrate: N/A + Stream #0:0: Video: h264 (Constrained Baseline), 1 reference frame, yuv420p(progressive, left), 2560x1440, 25 fps, 60 tbr, 1200k tbn +[out#0/null @ 0xaaab09e42f00] No explicit maps, mapping streams automatically... +[vost#0:0/wrapped_avframe @ 0xaaab09e77260] Created video stream from input stream 0:0 +[h264_mp4toannexb @ 0xaaab09e6e100] The input looks like it is Annex B already +mpp[30233]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +[h264_rkmpp @ 0xaaab09f55980] Picked up an existing RKMPP hardware device +Stream mapping: + Stream #0:0 -> #0:0 (h264 (h264_rkmpp) -> wrapped_avframe (native)) +Press [q] to stop, [?] for help +[h264_rkmpp @ 0xaaab09f55980] Noticed an info change +[h264_rkmpp @ 0xaaab09f55980] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +[h264_rkmpp @ 0xaaab09f55980] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +[graph 0 input from stream 0:0 @ 0xaaab09f264a0] w:2560 h:1440 pixfmt:drm_prime tb:1/1200000 fr:30/1 sar:0/1 +Output #0, null, to 'pipe:': + Metadata: + encoder : Lavf60.16.101 + Stream #0:0: Video: wrapped_avframe, 1 reference frame, drm_prime(tv, progressive, left), 2560x1440 (0x0), q=2-31, 200 kb/s, 30 fps, 30 tbn + Metadata: + encoder : Lavc60.31.102 wrapped_avframe +frame= 0 fps=0.0 q=-0.0 size= 0kB time=00:00:00.03 bitrate= 0.0kbits/s speed=29.3x [in#0/h264 @ 0xaaab09e3b470] EOF while reading input +[in#0/h264 @ 0xaaab09e3b470] Terminating demuxer thread +[vist#0:0/h264 @ 0xaaab09e42c30] Decoder thread received EOF packet +[vist#0:0/h264 @ 0xaaab09e42c30] Decoder returned EOF, finishing +[vist#0:0/h264 @ 0xaaab09e42c30] Terminating decoder thread +No more output streams to write to, finishing. +[out#0/null @ 0xaaab09e42f00] All streams finished +[out#0/null @ 0xaaab09e42f00] Terminating muxer thread +[out#0/null @ 0xaaab09e42f00] Output file #0 (pipe:): +[out#0/null @ 0xaaab09e42f00] Output stream #0:0 (video): 60 frames encoded; 60 packets muxed (28800 bytes); +[out#0/null @ 0xaaab09e42f00] Total: 60 packets (28800 bytes) muxed +[out#0/null @ 0xaaab09e42f00] video:28kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown +frame= 60 fps=0.0 q=-0.0 Lsize=N/A time=00:00:02.00 bitrate=N/A speed=6.91x +bench: utime=0.058s stime=0.062s rtime=0.290s +bench: maxrss=24448kB +[in#0/h264 @ 0xaaab09e3b470] Input file #0 (/home/armbian/vdi-build/rkmpp-experiment/test/1440p.h264): +[in#0/h264 @ 0xaaab09e3b470] Input stream #0:0 (video): 60 packets read (57854 bytes); 60 frames decoded; 0 decode errors; +[in#0/h264 @ 0xaaab09e3b470] Total: 60 packets (57854 bytes) demuxed +[AVIOContext @ 0xaaab09e3bb10] Statistics: 57854 bytes read, 0 seeks diff --git a/docs/evidence/moonlight-20261102/dependency-sha256.txt b/docs/evidence/moonlight-20261102/dependency-sha256.txt new file mode 100644 index 0000000..29b0d31 --- /dev/null +++ b/docs/evidence/moonlight-20261102/dependency-sha256.txt @@ -0,0 +1,42 @@ +9eba20198e9416da0be63cf8da2c34d943e6702b825528d1678eccfc7ab5b3ce /home/armbian/vdi-build/rkmpp-experiment/debs/libbrotli-dev_1.1.0-2ubuntu0.1_arm64.deb +892633787d2810eb326e3b3dc5ef43cc61fa5b58efdd8474cef71a7c706d8220 /home/armbian/vdi-build/rkmpp-experiment/debs/libdrm-dev_2.4.125-1ubuntu0.1~24.04.2_arm64.deb +99e364f5b5d5847feeb57bf4bd866331da93f16ba767c25286dd7f65e4b828ec /home/armbian/vdi-build/rkmpp-experiment/debs/libegl-dev_1.7.0-1build1_arm64.deb +887755d6aa91c12ee0eb3404b61f6dc138c4c59234aa2add0264baf270337708 /home/armbian/vdi-build/rkmpp-experiment/debs/libffi-dev_3.4.6-1build1_arm64.deb +bf051daf32337f64c23010f59bfca6e54810918531ba7757aa6797fa413ba1cb /home/armbian/vdi-build/rkmpp-experiment/debs/libfreetype-dev_2.13.2+dfsg-1ubuntu0.2_arm64.deb +f0faf030f4dc8b78420dbdefd3aa764b855aa6ff7fbe1b0851ba851c43c2fa08 /home/armbian/vdi-build/rkmpp-experiment/debs/libgbm-dev_25.2.8-0ubuntu0.24.04.4_arm64.deb +a7c9cd011c8e05fb573445560cab86d61583cda0f79c248fd73680e2d0618b54 /home/armbian/vdi-build/rkmpp-experiment/debs/libgles1_1.7.0-1build1_arm64.deb +df742afc0b66f24d80920b419723e072ab59d70726519d5994f50cf09e1f3961 /home/armbian/vdi-build/rkmpp-experiment/debs/libgles-dev_1.7.0-1build1_arm64.deb +7cf6c28c1895d6843e901f68dd2d90be5a9280bfef70e1e8dfaa2096e14c13ad /home/armbian/vdi-build/rkmpp-experiment/debs/libglib2.0-dev_2.80.0-6ubuntu3.10_arm64.deb +f6af2526f1a4b2264bbc7db25161f1d4f14ade79f731cd21a4c388d7e03debce /home/armbian/vdi-build/rkmpp-experiment/debs/libgraphite2-dev_1.3.14-2ubuntu0.24.04.1_arm64.deb +2897a334893c407502f3787fb1ca10ddfd2e35c05a19637396afcfd55873db8d /home/armbian/vdi-build/rkmpp-experiment/debs/libharfbuzz-dev_8.3.0-2build2_arm64.deb +4ef5f13cb3a4cbac89e50f48c70355a658911d8db2ff94f263daf5092074e010 /home/armbian/vdi-build/rkmpp-experiment/debs/libopus-dev_1.4-1build1_arm64.deb +d73f6246d98ad03283f6b9a30fd86da52ea360afbf5ed48c2e4eb995d04b87aa /home/armbian/vdi-build/rkmpp-experiment/debs/libpng-dev_1.6.43-5ubuntu0.7_arm64.deb +85957d625c083a85b7cade0c3696b99095eea9c03242a3452cb6d4b0cb882c3d /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5qmlworkerscript5_5.15.13+dfsg-1ubuntu0.1_arm64.deb +e10c14d16d3167b29ff1c69d955d3cde68ccb65dbc2f1362c0140fe88e55274f /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5quickcontrols2-5_5.15.13+dfsg-1_arm64.deb +97499cbc6dfed8ac3fc1e126861f4e4f66f5196d64eceb90810cd814a7a59069 /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5quickparticles5_5.15.13+dfsg-1ubuntu0.1_arm64.deb +66d440824d1ab8845e3422a63e248e650f0efc80a07f961b5842ccdb58c199c3 /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5quickshapes5_5.15.13+dfsg-1ubuntu0.1_arm64.deb +e5fc6fa488bef184069aa80e046991d8c75ec76d6f0b8e6580b27344b0543577 /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5quicktemplates2-5_5.15.13+dfsg-1_arm64.deb +73f7aa7231c06cc6e4e7d215421d60cca4835c4f94d1b69587b73cb41866d45a /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5quicktest5_5.15.13+dfsg-1ubuntu0.1_arm64.deb +813202055ca9743206d33e955cac3152a3bac7abbbc8254e01c18feda6e39d0d /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5svg5_5.15.13-1_arm64.deb +690245647bb8fd8ce21feaf8bece72c4cf64c85747fe6baa96dc20dd4eb95c1d /home/armbian/vdi-build/rkmpp-experiment/debs/libqt5svg5-dev_5.15.13-1_arm64.deb +3406a7a3cf98a633c9a1289eb20a31e03a04477afb95c8558f2de7f70abdd0bb /home/armbian/vdi-build/rkmpp-experiment/debs/libsdl2-2.0-0_2.30.0+dfsg-1ubuntu3.1_arm64.deb +edcb8ab3594488050f0a1c3fe983cf57d00ed6680ae173a8b919ef6fd99c3c3a /home/armbian/vdi-build/rkmpp-experiment/debs/libsdl2-dev_2.30.0+dfsg-1ubuntu3.1_arm64.deb +7a23ed32398355c4e771a8a1eae344f3cbbaca6deffe4172f8ce900dd4154687 /home/armbian/vdi-build/rkmpp-experiment/debs/libsdl2-ttf-2.0-0_2.22.0+dfsg-1_arm64.deb +e6a9ddf95bbb39bd55f9587fc874291d00cc8a5723b6d5a721bc50d646565926 /home/armbian/vdi-build/rkmpp-experiment/debs/libsdl2-ttf-dev_2.22.0+dfsg-1_arm64.deb +4b287709aaddb404233155f78c4f08bb943515065d99b8a205b7a147b76b4d5d /home/armbian/vdi-build/rkmpp-experiment/debs/libudev-dev_255.4-1ubuntu8.18_arm64.deb +a2cc274d08d3b966008d0a1f43545bc569c7934d383355829706fb25220b842e /home/armbian/vdi-build/rkmpp-experiment/debs/libwayland-bin_1.22.0-2.1build1_arm64.deb +6d94b723710aae35d0ff765978818601f4320c1dc4e0eec57e73b000943a1758 /home/armbian/vdi-build/rkmpp-experiment/debs/libwayland-dev_1.22.0-2.1build1_arm64.deb +213534d13ff7afd55155ae022d972faa6d2c343fcebb1e4517f37d7193b1b6d0 /home/armbian/vdi-build/rkmpp-experiment/debs/libx264-dev_2%3a0.164.3108+git31e19f9-1_arm64.deb +de01e5d50d114ddc5419d61cde552d8a956a2dc9e6c004ce50b7f37a02a47c1d /home/armbian/vdi-build/rkmpp-experiment/debs/libxkbcommon-dev_1.6.0-1build1_arm64.deb +80edfdbc1cd71202ec56d0643e24f75745f1f60bee0cfaef7afa8a40b290ddf2 /home/armbian/vdi-build/rkmpp-experiment/debs/qml-module-qtqml-models2_5.15.13+dfsg-1ubuntu0.1_arm64.deb +5315cda68ae300ba6938ac83dbb398644419214a8febf2ed988500d8bc4b3d20 /home/armbian/vdi-build/rkmpp-experiment/debs/qml-module-qtquick2_5.15.13+dfsg-1ubuntu0.1_arm64.deb +a5c57eefe7ec2dafc5f0d9410fef4a09320349eece173e406be6b73eb4eab985 /home/armbian/vdi-build/rkmpp-experiment/debs/qml-module-qtquick-controls2_5.15.13+dfsg-1_arm64.deb +cecf088b56cbe3df083bf3b18d7da264d6ac270bb93796e749b3bd5497253340 /home/armbian/vdi-build/rkmpp-experiment/debs/qml-module-qtquick-layouts_5.15.13+dfsg-1ubuntu0.1_arm64.deb +775d0c95e6ab30a6002d6c0a0613b4c4b9d9d91616804701f7c707706c51bb0b /home/armbian/vdi-build/rkmpp-experiment/debs/qml-module-qtquick-templates2_5.15.13+dfsg-1_arm64.deb +af63b41209ee631ba5a5c7eee3976b4342373fd6cd6e47a51467834ac8aac05b /home/armbian/vdi-build/rkmpp-experiment/debs/qml-module-qtquick-window2_5.15.13+dfsg-1ubuntu0.1_arm64.deb +02313d79aa812e29f6b0494bcbad04e89df8734368e5d68485ac29c31e48b1a7 /home/armbian/vdi-build/rkmpp-experiment/debs/qt5-qmltooling-plugins_5.15.13+dfsg-1ubuntu0.1_arm64.deb +a7972aee9593b17742461d0b81e1eec1aae0d50ff76257b2bec01844c523be2f /home/armbian/vdi-build/rkmpp-experiment/debs/qtdeclarative5-dev_5.15.13+dfsg-1ubuntu0.1_arm64.deb +f4235926cc915848adc215ff279b108a19222d5a0e8f4d28210db0e8ffee5848 /home/armbian/vdi-build/rkmpp-experiment/debs/qtdeclarative5-dev-tools_5.15.13+dfsg-1ubuntu0.1_arm64.deb +852dba1e49327be8e609b9235ac5a7374fd4538d4196752075989f6bb357e590 /home/armbian/vdi-build/rkmpp-experiment/debs/qtquickcontrols2-5-dev_5.15.13+dfsg-1_arm64.deb +e4fa5eb09a9f3eee12a863718badfc51a986cda32e53e37e841b76756e5caebc /home/armbian/vdi-build/rkmpp-experiment/debs/wayland-protocols_1.45-1~ubuntu0.24.04.2_all.deb +5ad30e35e9bb0a66a1af4d1bac3d9f2035658040affec533e058001259e2796f /home/armbian/vdi-build/rkmpp-experiment/debs/zlib1g-dev_1%3a1.3.dfsg-3.1ubuntu2.3_arm64.deb diff --git a/docs/evidence/moonlight-20261102/generate-1440p.txt b/docs/evidence/moonlight-20261102/generate-1440p.txt new file mode 100644 index 0000000..e8ff62f --- /dev/null +++ b/docs/evidence/moonlight-20261102/generate-1440p.txt @@ -0,0 +1,10 @@ +[libx264 @ 0xaaaaf7134470] using cpu capabilities: ARMv8 NEON +[libx264 @ 0xaaaaf7134470] profile Constrained Baseline, level 5.0, 4:2:0, 8-bit +[libx264 @ 0xaaaaf7134470] frame I:1 Avg QP:20.00 size: 15901 +[libx264 @ 0xaaaaf7134470] frame P:59 Avg QP:16.00 size: 711 +[libx264 @ 0xaaaaf7134470] mb I I16..4: 100.0% 0.0% 0.0% +[libx264 @ 0xaaaaf7134470] mb P I16..4: 1.3% 0.0% 0.0% P16..4: 2.8% 0.0% 0.0% 0.0% 0.0% skip:95.9% +[libx264 @ 0xaaaaf7134470] coded y,uvDC,uvAC intra: 0.4% 0.0% 0.0% inter: 0.0% 0.0% 0.0% +[libx264 @ 0xaaaaf7134470] i16 v,h,dc,p: 0% 99% 0% 0% +[libx264 @ 0xaaaaf7134470] i8c dc,h,v,p: 100% 0% 0% 0% +[libx264 @ 0xaaaaf7134470] kb/s:231.42 diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-disconnect.png b/docs/evidence/moonlight-20261102/kiosk-rkmpp-disconnect.png new file mode 100644 index 0000000..a88da16 Binary files /dev/null and b/docs/evidence/moonlight-20261102/kiosk-rkmpp-disconnect.png differ diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-final-desktop.png b/docs/evidence/moonlight-20261102/kiosk-rkmpp-final-desktop.png new file mode 100644 index 0000000..d992b9f Binary files /dev/null and b/docs/evidence/moonlight-20261102/kiosk-rkmpp-final-desktop.png differ diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-first-connect.png b/docs/evidence/moonlight-20261102/kiosk-rkmpp-first-connect.png new file mode 100644 index 0000000..898e3a1 Binary files /dev/null and b/docs/evidence/moonlight-20261102/kiosk-rkmpp-first-connect.png differ diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-monitor.txt b/docs/evidence/moonlight-20261102/kiosk-rkmpp-monitor.txt new file mode 100644 index 0000000..9badb91 --- /dev/null +++ b/docs/evidence/moonlight-20261102/kiosk-rkmpp-monitor.txt @@ -0,0 +1,13 @@ +[monitor 2026-11-02 20:10:05] 看门狗启动,监控: /home/armbian/vdi-terminal/bin/kiosk-shell +[monitor 2026-11-02 20:10:05] 主进程已启动, pid=63682 +vdi_connection attempt=1 desktop=desk-win10-01 phase=ticket result=started phase_ms=0 total_ms=0 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=ticket result=ready phase_ms=19 total_ms=19 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=process_start result=started phase_ms=0 total_ms=19 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=process_start result=ready phase_ms=110 total_ms=130 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=stream_ready result=ready phase_ms=9940 total_ms=10071 error= +vdi_connection attempt=1 desktop=desk-win10-01 phase=session result=finished phase_ms=170912 total_ms=171043 error= +vdi_connection attempt=3 desktop=desk-win10-01 phase=ticket result=started phase_ms=0 total_ms=0 error= +vdi_connection attempt=3 desktop=desk-win10-01 phase=ticket result=ready phase_ms=1 total_ms=1 error= +vdi_connection attempt=3 desktop=desk-win10-01 phase=process_start result=started phase_ms=0 total_ms=2 error= +vdi_connection attempt=3 desktop=desk-win10-01 phase=process_start result=ready phase_ms=62 total_ms=64 error= +vdi_connection attempt=3 desktop=desk-win10-01 phase=stream_ready result=ready phase_ms=9638 total_ms=9702 error= diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-reconnect.png b/docs/evidence/moonlight-20261102/kiosk-rkmpp-reconnect.png new file mode 100644 index 0000000..a1e9706 Binary files /dev/null and b/docs/evidence/moonlight-20261102/kiosk-rkmpp-reconnect.png differ diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-session.png b/docs/evidence/moonlight-20261102/kiosk-rkmpp-session.png new file mode 100644 index 0000000..6d2dfa3 Binary files /dev/null and b/docs/evidence/moonlight-20261102/kiosk-rkmpp-session.png differ diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-validation.txt b/docs/evidence/moonlight-20261102/kiosk-rkmpp-validation.txt new file mode 100644 index 0000000..a35ba06 --- /dev/null +++ b/docs/evidence/moonlight-20261102/kiosk-rkmpp-validation.txt @@ -0,0 +1,452 @@ + +[VDI launch] +profile=low-latency +program=/home/armbian/vdi-terminal/bin/vdi-moonlight-rkmpp +decoder=hardware +codec=H.264 +vsync=0 +frame-pacing=0 +renderer=opengles2 +video-driver= +args=stream 192.168.1.70 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --keep-awake --no-vsync --no-frame-pacing --audio-on-host +00:00:00 - SDL Info (0): Compiled with SDL 2.30.0 +00:00:00 - SDL Info (0): Running with SDL 2.30.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:02 - SDL Info (0): Qt UI screen is at (0,0) +00:00:02 - SDL Info (0): SDL found matching display 0 +00:00:02 - Qt Info: Server GPU: "" +00:00:02 - Qt Info: Server GFE version: "3.23.0.74" +00:00:02 - SDL Info (0): Video bitrate: 30000 kbps +00:00:02 - SDL Info (0): Audio channel count: 2 +00:00:02 - SDL Info (0): Audio channel mask: 3 +00:00:02 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf977dda0] The input looks like it is Annex B already +mpp[64569]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf9478020] The input looks like it is Annex B already +mpp[64569]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Noticed an info change +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf94e7ed0] Received a frame +00:00:03 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:03 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:03 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:06 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:06 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:06 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=1f4223efe39e4eecad4ee7e90ad131e8&appid=881448767&mode=2560x1440x30&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=1&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:07 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:07 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:07 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:07 - Qt Info: Interface MTU: 1500 +00:00:07 - SDL Info (0): RTSP port: 48010 +00:00:07 - SDL Info (0): Initializing platform... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Resolving host name... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing audio stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting RTSP handshake... +00:00:07 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:07 - SDL Info (0): Audio port: 48000 +00:00:07 - SDL Info (0): Video port: 47998 +00:00:07 - SDL Info (0): Control port: 47999 +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing video stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting video stream... +00:00:07 - SDL Info (0): Video stream is 2560x1440x30 (format 0x1) +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting audio stream... +00:00:07 - SDL Info (0): Received first video packet after 0 ms +00:00:07 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): SDL audio driver: pulseaudio +00:00:07 - SDL Info (0): Audio stream has 2 channels +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Qt UI screen is at (0,0) +00:00:07 - SDL Info (0): SDL found matching display 0 +00:00:07 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:07 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:07 - SDL Info (0): V-sync disabled +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf96822d0] The input looks like it is Annex B already +mpp[64569]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Picked up an existing RKMPP hardware device +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Wrote 390 bytes to decoder +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Received a frame +00:00:08 - SDL Info (0): Not reusing test decoder for h264_rkmpp +00:00:08 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:08 - SDL Info (0): GPU driver: rockchip +00:00:08 - SDL Info (0): Direct rendering via DRM is disabled +00:00:08 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Frame pacing disabled: target 60 Hz with 30 FPS stream +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf9fc4600] The input looks like it is Annex B already +mpp[64569]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Picked up an existing RKMPP hardware device +00:00:08 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:08 - SDL Info (0): Using H.264 SPS fixup +00:00:08 - SDL Info (0): Renderer 'EGL/GLES' with 'DRM' backend chosen +00:00:08 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:08 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Wrote 48152 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Wrote 15128 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Wrote 17880 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Wrote 16504 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf9493c60] Received a frame +00:00:08 - SDL Info (0): EGLRenderer: EGLImage pixel format: 179 +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_opaque.frag" at ":/data/egl_opaque.frag" +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_overlay.frag" at ":/data/egl_overlay.frag" +00:02:50 - SDL Info (0): Detected quit key combo +00:02:50 - SDL Info (0): Quit event received +00:02:50 - SDL Info (0): Raising 3 keys +00:02:50 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 25.52 FPS +Decoding frame rate: 25.52 FPS +Rendering frame rate: 25.45 FPS +Host processing latency min/max/average: 34.0/127.9/45.4 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.10% +Average network latency: 8 ms (variance: 4 ms) +Average decoding time: 7.06 ms +Average frame queue delay: 0.33 ms +Average rendering time (including monitor V-sync latency): 1.91 ms +00:02:50 - SDL Info (0): Stopping input stream... +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Stopping audio stream... +00:02:50 - SDL Info (0): No audio traffic was ever received from the host! +00:02:50 - SDL Info (0): ENet wait interrupted +00:02:50 - SDL Info (0): Control stream connection failed: 4 +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Stopping video stream... +00:02:50 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Stopping control stream... +00:02:50 - SDL Info (0): Loaded 436 new gamepad mappings +00:02:50 - SDL Info (0): ENet peer acknowledged disconnection +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Cleaning up input stream... +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Cleaning up video stream... +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Cleaning up control stream... +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Cleaning up audio stream... +00:02:50 - SDL Info (0): done +00:02:50 - SDL Info (0): Cleaning up platform... +00:02:50 - SDL Info (0): done + +[VDI launch] +profile=low-latency +program=/home/armbian/vdi-terminal/bin/vdi-moonlight-rkmpp +decoder=hardware +codec=H.264 +vsync=0 +frame-pacing=0 +renderer=opengles2 +video-driver= +args=stream 192.168.1.70 Desktop --resolution 2560x1440 --fps 30 --bitrate 30000 --display-mode fullscreen --video-codec H.264 --video-decoder hardware --absolute-mouse --keep-awake --no-vsync --no-frame-pacing --audio-on-host +00:00:00 - SDL Info (0): Compiled with SDL 2.30.0 +00:00:00 - SDL Info (0): Running with SDL 2.30.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:01 - SDL Info (0): Qt UI screen is at (0,0) +00:00:01 - SDL Info (0): SDL found matching display 0 +00:00:01 - Qt Info: Server GPU: "" +00:00:01 - Qt Info: Server GFE version: "3.23.0.74" +00:00:02 - SDL Info (0): Video bitrate: 30000 kbps +00:00:02 - SDL Info (0): Audio channel count: 2 +00:00:02 - SDL Info (0): Audio channel mask: 3 +00:00:02 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaae8148f80] The input looks like it is Annex B already +mpp[66385]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaae859afc0] The input looks like it is Annex B already +mpp[66385]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:03 - Qt Info: Gamepad mappings are up to date +00:00:06 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:06 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:06 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=90dfc80ae65d4b1bb02c722d2a22325f&appid=881448767&mode=2560x1440x30&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=1&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:06 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:06 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:06 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:06 - Qt Info: Interface MTU: 1500 +00:00:06 - SDL Info (0): RTSP port: 48010 +00:00:06 - SDL Info (0): Initializing platform... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Resolving host name... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing audio stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting RTSP handshake... +00:00:06 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:06 - SDL Info (0): Audio port: 48000 +00:00:06 - SDL Info (0): Video port: 47998 +00:00:06 - SDL Info (0): Control port: 47999 +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing video stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting video stream... +00:00:07 - SDL Info (0): Video stream is 2560x1440x30 (format 0x1) +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting audio stream... +00:00:07 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): SDL audio driver: pulseaudio +00:00:07 - SDL Info (0): Audio stream has 2 channels +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Received first video packet after 0 ms +00:00:07 - SDL Info (0): Qt UI screen is at (0,0) +00:00:07 - SDL Info (0): SDL found matching display 0 +00:00:07 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:07 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:07 - SDL Info (0): V-sync disabled +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:07 - SDL Info (0): Video decode unit queue overflow +00:00:07 - SDL Info (0): IDR frame request sent +00:00:07 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaae89dd090] The input looks like it is Annex B already +mpp[66385]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Picked up an existing RKMPP hardware device +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Received a frame +00:00:08 - SDL Info (0): Not reusing test decoder for h264_rkmpp +00:00:08 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:08 - SDL Info (0): GPU driver: rockchip +00:00:08 - SDL Info (0): Direct rendering via DRM is disabled +00:00:08 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Frame pacing disabled: target 60 Hz with 30 FPS stream +00:00:08 - SDL Info (0): Using DRM renderer +mpp[66385]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaae89d8820] The input looks like it is Annex B already +00:00:08 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Picked up an existing RKMPP hardware device +00:00:08 - SDL Info (0): Using H.264 SPS fixup +00:00:08 - SDL Info (0): Renderer 'EGL/GLES' with 'DRM' backend chosen +00:00:08 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:08 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 46776 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 11000 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Wrote 11000 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaae7adb140] Received a frame +00:00:08 - SDL Info (0): EGLRenderer: EGLImage pixel format: 179 +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_opaque.frag" at ":/data/egl_opaque.frag" +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_overlay.frag" at ":/data/egl_overlay.frag" diff --git a/docs/evidence/moonlight-20261102/kiosk-rkmpp-window-drag.png b/docs/evidence/moonlight-20261102/kiosk-rkmpp-window-drag.png new file mode 100644 index 0000000..cedd328 Binary files /dev/null and b/docs/evidence/moonlight-20261102/kiosk-rkmpp-window-drag.png differ diff --git a/docs/evidence/moonlight-20261102/low-latency-motion.png b/docs/evidence/moonlight-20261102/low-latency-motion.png new file mode 100644 index 0000000..9b6dc62 Binary files /dev/null and b/docs/evidence/moonlight-20261102/low-latency-motion.png differ diff --git a/docs/evidence/moonlight-20261102/low-latency-software.txt b/docs/evidence/moonlight-20261102/low-latency-software.txt new file mode 100644 index 0000000..5cc957e --- /dev/null +++ b/docs/evidence/moonlight-20261102/low-latency-software.txt @@ -0,0 +1,249 @@ +00:00:00 - SDL Info (0): Compiled with SDL 2.33.0 +00:00:00 - SDL Info (0): Running with SDL 2.33.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 435 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:01 - SDL Info (0): Qt UI screen is at (0,0) +00:00:01 - SDL Info (0): SDL found matching display 0 +00:00:01 - Qt Info: Server GPU: "" +00:00:01 - Qt Info: Server GFE version: "3.23.0.74" +00:00:01 - SDL Info (0): Video bitrate: 30000 kbps +00:00:01 - SDL Info (0): Audio channel count: 2 +00:00:01 - SDL Info (0): Audio channel mask: 3 +00:00:02 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 435 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Decoding VUI +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format drm_prime chosen by get_format(). +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Invalid setup for format drm_prime: missing configuration. +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format drm_prime not usable, retrying get_format() without it. +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format yuv420p chosen by get_format(). +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:02 - SDL Warn (0): EGLImage export requires hardware-backed frames +00:00:02 - SDL Warn (0): Backend failed to export EGL image for test frame +00:00:02 - SDL Warn (0): Test decode failed (testRenderFrame) +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Decoding VUI +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format yuv420p chosen by get_format(). +00:00:02 - FFmpeg: [h264 @ 0xaaaaf2adf910] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - SDL Info (0): Encoder configured for 4 slices per frame +00:00:02 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:02 - Qt Warning: 你选择的用于强制软件解码的设置可能会导致流式传输性能不佳。 +00:00:03 - Qt Info: Processing new PC "DESKTOP-Q072ICM.local." from mDNS with local address "192.168.1.70:47989" +00:00:03 - Qt Info: Executing request: "http://192.168.1.70:47989/serverinfo?uniqueid=0123456789ABCDEF&uuid=ebc03298cf8c48b08710bdc9e8df351a" +00:00:03 - Qt Info: Executing request: "https://192.168.1.70:47984/serverinfo?uniqueid=95da1945b8736669&uuid=81445f65f6e8426bbf57e183f3bda211" +00:00:05 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:05 - SDL Info (0): Loaded 435 new gamepad mappings +00:00:05 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=b788d125e836493a85f7eacede1e24eb&appid=881448767&mode=2560x1440x20&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=0&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:06 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:06 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:06 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:06 - Qt Info: Interface MTU: 1500 +00:00:06 - SDL Info (0): RTSP port: 48010 +00:00:06 - SDL Info (0): Initializing platform... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Resolving host name... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing audio stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting RTSP handshake... +00:00:06 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:06 - SDL Info (0): Audio port: 48000 +00:00:06 - SDL Info (0): Video port: 47998 +00:00:06 - SDL Info (0): Control port: 47999 +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing control stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing video stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing input stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting control stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting video stream... +00:00:06 - SDL Info (0): Video stream is 2560x1440x20 (format 0x1) +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting audio stream... +00:00:06 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:06 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:06 - SDL Info (0): SDL audio driver: pulseaudio +00:00:06 - SDL Info (0): Audio stream has 2 channels +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting input stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Received first video packet after 100 ms +00:00:06 - SDL Info (0): Qt UI screen is at (0,0) +00:00:06 - SDL Info (0): SDL found matching display 0 +00:00:06 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:07 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:07 - SDL Info (0): V-sync disabled +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:07 - SDL Info (0): Video decode unit queue overflow +00:00:07 - SDL Info (0): IDR frame request sent +00:00:07 - SDL Info (0): Waiting for IDR frame +00:00:07 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:07 - SDL Info (0): Frame pacing disabled: target 60 Hz with 20 FPS stream +00:00:07 - SDL Info (0): Using DRM renderer +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Decoding VUI +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format drm_prime chosen by get_format(). +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Invalid setup for format drm_prime: missing configuration. +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format drm_prime not usable, retrying get_format() without it. +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format yuv420p chosen by get_format(). +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:07 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:07 - SDL Warn (0): EGLImage export requires hardware-backed frames +00:00:07 - SDL Warn (0): Backend failed to export EGL image for test frame +00:00:07 - SDL Warn (0): Test decode failed (testRenderFrame) +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): Frame pacing disabled: target 60 Hz with 20 FPS stream +00:00:07 - SDL Info (0): SDL renderer backend: opengles2 +00:00:07 - SDL Info (0): SDL renderer backend requires main thread rendering +00:00:07 - SDL Info (0): Using DRM renderer +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Decoding VUI +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format yuv420p chosen by get_format(). +00:00:07 - FFmpeg: [h264 @ 0xaaaaf2adf910] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:08 - SDL Info (0): Test decode successful +00:00:08 - SDL Info (0): Using H.264 SPS fixup +00:00:08 - SDL Info (0): Renderer 'SDL' with 'DRM' backend chosen +00:00:08 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] Decoding VUI +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] Format yuvj420p chosen by get_format(). +00:00:08 - FFmpeg: [h264 @ 0xaaaaf2adf910] Reinit context to 2560x1440, pix_fmt: yuvj420p +00:00:08 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:08 - SDL Info (0): IDR frame request sent +00:03:27 - SDL Info (0): Raising 3 keys +00:03:27 - SDL Info (0): Detected quit key combo +00:03:27 - SDL Info (0): Quit event received +00:03:27 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 17.07 FPS +Decoding frame rate: 17.07 FPS +Rendering frame rate: 17.01 FPS +Host processing latency min/max/average: 24.1/104.8/35.4 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.29% +Average network latency: 11 ms (variance: 6 ms) +Average decoding time: 16.96 ms +Average frame queue delay: 1.14 ms +Average rendering time (including monitor V-sync latency): 10.87 ms +00:03:28 - SDL Info (0): Stopping input stream... +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Stopping audio stream... +00:03:28 - SDL Info (0): No audio traffic was ever received from the host! +00:03:28 - SDL Info (0): ENet wait interrupted +00:03:28 - SDL Info (0): Control stream connection failed: 4 +00:03:28 - Qt Info: Gamepad mappings are up to date +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Stopping video stream... +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Stopping control stream... +00:03:28 - SDL Info (0): ENet peer acknowledged disconnection +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Cleaning up input stream... +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Cleaning up video stream... +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Cleaning up control stream... +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Cleaning up audio stream... +00:03:28 - SDL Info (0): done +00:03:28 - SDL Info (0): Cleaning up platform... +00:03:28 - SDL Info (0): done +00:03:28 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:03:28 - SDL Info (0): Loaded 435 new gamepad mappings diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-20-smooth.txt b/docs/evidence/moonlight-20261102/rkmpp-egl-20-smooth.txt new file mode 100644 index 0000000..247617c --- /dev/null +++ b/docs/evidence/moonlight-20261102/rkmpp-egl-20-smooth.txt @@ -0,0 +1,237 @@ +00:00:00 - SDL Info (0): Compiled with SDL 2.30.0 +00:00:00 - SDL Info (0): Running with SDL 2.30.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:02 - SDL Info (0): Qt UI screen is at (0,0) +00:00:02 - SDL Info (0): SDL found matching display 0 +00:00:02 - Qt Info: Server GPU: "" +00:00:02 - Qt Info: Server GFE version: "3.23.0.74" +00:00:02 - SDL Info (0): Video bitrate: 30000 kbps +00:00:02 - SDL Info (0): Audio channel count: 2 +00:00:02 - SDL Info (0): Audio channel mask: 3 +00:00:02 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaaea8769f0] The input looks like it is Annex B already +mpp[51478]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Received a frame +00:00:03 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:03 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:03 - SDL Info (0): V-sync disabled +00:00:03 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:03 - SDL Info (0): GPU driver: rockchip +00:00:03 - SDL Info (0): Direct rendering via DRM is disabled +00:00:03 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:03 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:03 - SDL Info (0): Using DRM renderer +00:00:03 - FFmpeg: [h264_mp4toannexb @ 0xaaaaea88ab90] The input looks like it is Annex B already +mpp[51478]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Picked up an existing RKMPP hardware device +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Noticed an info change +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Received a frame +00:00:03 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:03 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:03 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:03 - Qt Info: Processing new PC "DESKTOP-Q072ICM.local." from mDNS with local address "192.168.1.70:47989" +00:00:03 - Qt Info: Executing request: "http://192.168.1.70:47989/serverinfo?uniqueid=0123456789ABCDEF&uuid=2c210dd36f6e44baaef56040d444099c" +00:00:03 - Qt Info: Executing request: "https://192.168.1.70:47984/serverinfo?uniqueid=95da1945b8736669&uuid=5dc654e7557140c5ad4a28a3e0cbf0e7" +00:00:05 - Qt Info: Gamepad mappings are up to date +00:00:07 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:07 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:07 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=d326392167b5421ab723a1fd3511fae0&appid=881448767&mode=2560x1440x20&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=1&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:07 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:07 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:07 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:07 - Qt Info: Interface MTU: 1500 +00:00:07 - SDL Info (0): RTSP port: 48010 +00:00:07 - SDL Info (0): Initializing platform... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Resolving host name... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing audio stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting RTSP handshake... +00:00:07 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:07 - SDL Info (0): Audio port: 48000 +00:00:07 - SDL Info (0): Video port: 47998 +00:00:07 - SDL Info (0): Control port: 47999 +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing video stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting video stream... +00:00:07 - SDL Info (0): Video stream is 2560x1440x20 (format 0x1) +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting audio stream... +00:00:07 - SDL Info (0): Received first video packet after 0 ms +00:00:07 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): SDL audio driver: pulseaudio +00:00:07 - SDL Info (0): Audio stream has 2 channels +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Qt UI screen is at (0,0) +00:00:07 - SDL Info (0): SDL found matching display 0 +00:00:08 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:08 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:08 - SDL Info (0): V-sync enabled +00:00:08 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:08 - SDL Info (0): GPU driver: rockchip +00:00:08 - SDL Info (0): Direct rendering via DRM is disabled +00:00:08 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaaeb135880] The input looks like it is Annex B already +mpp[51478]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Picked up an existing RKMPP hardware device +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Noticed an info change +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 390 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Received a frame +00:00:09 - SDL Info (0): Not reusing test decoder for h264_rkmpp +00:00:09 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:09 - SDL Info (0): GPU driver: rockchip +00:00:09 - SDL Info (0): Direct rendering via DRM is disabled +00:00:09 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:09 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:09 - SDL Info (0): Frame pacing: target 60 Hz with 20 FPS stream +00:00:09 - SDL Info (0): Using DRM renderer +00:00:09 - FFmpeg: [h264_mp4toannexb @ 0xaaaaeaef0980] The input looks like it is Annex B already +mpp[51478]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:09 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Picked up an existing RKMPP hardware device +00:00:09 - SDL Info (0): Using H.264 SPS fixup +00:00:09 - SDL Info (0): Renderer 'EGL/GLES' with 'DRM' backend chosen +00:00:09 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:09 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 55032 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 13752 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 19256 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Wrote 15128 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Noticed an info change +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:09 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:09 - SDL Info (0): IDR frame request sent +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaea1ff9d0] Received a frame +00:00:09 - SDL Info (0): EGLRenderer: EGLImage pixel format: 179 +00:00:09 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:09 - Qt Info: Found "egl_opaque.frag" at ":/data/egl_opaque.frag" +00:00:09 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:09 - Qt Info: Found "egl_overlay.frag" at ":/data/egl_overlay.frag" +00:02:21 - SDL Info (0): Raising 3 keys +00:02:21 - SDL Info (0): Detected quit key combo +00:02:21 - SDL Info (0): Quit event received +00:02:21 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 19.14 FPS +Decoding frame rate: 19.14 FPS +Rendering frame rate: 18.97 FPS +Host processing latency min/max/average: 26.6/104.7/38.4 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.20% +Average network latency: 8 ms (variance: 8 ms) +Average decoding time: 10.53 ms +Average frame queue delay: 2.78 ms +Average rendering time (including monitor V-sync latency): 2.54 ms +00:02:21 - SDL Info (0): Stopping input stream... +00:02:21 - Qt Info: Latest supported GFE server: "99.99.99.99" +00:02:21 - SDL Info (0): ENet wait interrupted +00:02:21 - SDL Info (0): Control stream connection failed: 4 +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Stopping audio stream... +00:02:21 - SDL Info (0): No audio traffic was ever received from the host! +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Stopping video stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Stopping control stream... +00:02:21 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:02:21 - SDL Info (0): Loaded 436 new gamepad mappings +00:02:21 - SDL Info (0): ENet peer acknowledged disconnection +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up input stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up video stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up control stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up audio stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up platform... +00:02:21 - SDL Info (0): done diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-30-lowlatency-motion.png b/docs/evidence/moonlight-20261102/rkmpp-egl-30-lowlatency-motion.png new file mode 100644 index 0000000..c3fd456 Binary files /dev/null and b/docs/evidence/moonlight-20261102/rkmpp-egl-30-lowlatency-motion.png differ diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-30-lowlatency.txt b/docs/evidence/moonlight-20261102/rkmpp-egl-30-lowlatency.txt new file mode 100644 index 0000000..793bbc7 --- /dev/null +++ b/docs/evidence/moonlight-20261102/rkmpp-egl-30-lowlatency.txt @@ -0,0 +1,240 @@ +00:00:00 - SDL Info (0): Compiled with SDL 2.30.0 +00:00:00 - SDL Info (0): Running with SDL 2.30.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:02 - SDL Info (0): Qt UI screen is at (0,0) +00:00:02 - SDL Info (0): SDL found matching display 0 +00:00:02 - Qt Info: Server GPU: "" +00:00:02 - Qt Info: Server GFE version: "3.23.0.74" +00:00:02 - SDL Info (0): Video bitrate: 30000 kbps +00:00:02 - SDL Info (0): Audio channel count: 2 +00:00:02 - SDL Info (0): Audio channel mask: 3 +00:00:02 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf99a27a0] The input looks like it is Annex B already +mpp[54250]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf995fa90] The input looks like it is Annex B already +mpp[54250]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaaf92e8420] Received a frame +00:00:03 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:03 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:03 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:03 - Qt Info: Processing new PC "DESKTOP-Q072ICM.local." from mDNS with local address "192.168.1.70:47989" +00:00:03 - Qt Info: Executing request: "http://192.168.1.70:47989/serverinfo?uniqueid=0123456789ABCDEF&uuid=384e05c9dbdc467aa7c65964c14160df" +00:00:03 - Qt Info: Executing request: "https://192.168.1.70:47984/serverinfo?uniqueid=95da1945b8736669&uuid=3a95f785091741c090fdffd7d56979e7" +00:00:04 - Qt Info: Gamepad mappings are up to date +00:00:06 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:06 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:06 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=b7dffe4902d54022a33fa19a3cf2b331&appid=881448767&mode=2560x1440x30&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=1&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:07 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:07 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:07 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:07 - Qt Info: Interface MTU: 1500 +00:00:07 - SDL Info (0): RTSP port: 48010 +00:00:07 - SDL Info (0): Initializing platform... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Resolving host name... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing audio stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting RTSP handshake... +00:00:07 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:07 - SDL Info (0): Audio port: 48000 +00:00:07 - SDL Info (0): Video port: 47998 +00:00:07 - SDL Info (0): Control port: 47999 +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing video stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting video stream... +00:00:07 - SDL Info (0): Video stream is 2560x1440x30 (format 0x1) +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting audio stream... +00:00:07 - SDL Info (0): Received first video packet after 0 ms +00:00:07 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): SDL audio driver: pulseaudio +00:00:07 - SDL Info (0): Audio stream has 2 channels +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Qt UI screen is at (0,0) +00:00:07 - SDL Info (0): SDL found matching display 0 +00:00:07 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:08 - SDL Info (0): V-sync disabled +00:00:08 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:08 - SDL Info (0): GPU driver: rockchip +00:00:08 - SDL Info (0): Direct rendering via DRM is disabled +00:00:08 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf96e2d90] The input looks like it is Annex B already +mpp[54250]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Picked up an existing RKMPP hardware device +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Wrote 390 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Wrote 390 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Noticed an info change +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Wrote 390 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Received a frame +00:00:09 - SDL Info (0): Not reusing test decoder for h264_rkmpp +00:00:09 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:09 - SDL Info (0): GPU driver: rockchip +00:00:09 - SDL Info (0): Direct rendering via DRM is disabled +00:00:09 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:09 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:09 - SDL Info (0): Frame pacing disabled: target 60 Hz with 30 FPS stream +00:00:09 - SDL Info (0): Using DRM renderer +00:00:09 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf96f1610] The input looks like it is Annex B already +mpp[54250]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Picked up an existing RKMPP hardware device +00:00:09 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:09 - SDL Info (0): Using H.264 SPS fixup +00:00:09 - SDL Info (0): Renderer 'EGL/GLES' with 'DRM' backend chosen +00:00:09 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:09 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Wrote 46776 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Wrote 19256 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Wrote 15128 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Wrote 16504 bytes to decoder +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Noticed an info change +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:09 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:09 - SDL Info (0): IDR frame request sent +00:00:09 - FFmpeg: [h264_rkmpp @ 0xaaaaf99a26d0] Received a frame +00:00:09 - SDL Info (0): EGLRenderer: EGLImage pixel format: 179 +00:00:09 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:09 - Qt Info: Found "egl_opaque.frag" at ":/data/egl_opaque.frag" +00:00:09 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:09 - Qt Info: Found "egl_overlay.frag" at ":/data/egl_overlay.frag" +00:02:20 - SDL Info (0): Detected quit key combo +00:02:20 - SDL Info (0): Quit event received +00:02:20 - SDL Info (0): Raising 3 keys +00:02:20 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 23.56 FPS +Decoding frame rate: 23.56 FPS +Rendering frame rate: 23.46 FPS +Host processing latency min/max/average: 29.7/145.8/46.8 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.10% +Average network latency: 4 ms (variance: 2 ms) +Average decoding time: 9.11 ms +Average frame queue delay: 2.39 ms +Average rendering time (including monitor V-sync latency): 5.41 ms +00:02:20 - SDL Info (0): Stopping input stream... +00:02:20 - SDL Info (0): done +00:02:20 - Qt Info: Latest supported GFE server: "99.99.99.99" +00:02:20 - SDL Info (0): Stopping audio stream... +00:02:20 - SDL Info (0): No audio traffic was ever received from the host! +00:02:20 - SDL Info (0): ENet wait interrupted +00:02:20 - SDL Info (0): Control stream connection failed: 4 +00:02:20 - SDL Info (0): done +00:02:20 - SDL Info (0): Stopping video stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Stopping control stream... +00:02:21 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:02:21 - SDL Info (0): Loaded 436 new gamepad mappings +00:02:21 - SDL Info (0): ENet peer acknowledged disconnection +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up input stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up video stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up control stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up audio stream... +00:02:21 - SDL Info (0): done +00:02:21 - SDL Info (0): Cleaning up platform... +00:02:21 - SDL Info (0): done diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-30-motion.png b/docs/evidence/moonlight-20261102/rkmpp-egl-30-motion.png new file mode 100644 index 0000000..be91065 Binary files /dev/null and b/docs/evidence/moonlight-20261102/rkmpp-egl-30-motion.png differ diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-30-responsive-motion.png b/docs/evidence/moonlight-20261102/rkmpp-egl-30-responsive-motion.png new file mode 100644 index 0000000..375d19e Binary files /dev/null and b/docs/evidence/moonlight-20261102/rkmpp-egl-30-responsive-motion.png differ diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-30-smooth.txt b/docs/evidence/moonlight-20261102/rkmpp-egl-30-smooth.txt new file mode 100644 index 0000000..3d452c4 --- /dev/null +++ b/docs/evidence/moonlight-20261102/rkmpp-egl-30-smooth.txt @@ -0,0 +1,240 @@ +00:00:00 - SDL Info (0): Compiled with SDL 2.30.0 +00:00:00 - SDL Info (0): Running with SDL 2.30.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:01 - SDL Info (0): Qt UI screen is at (0,0) +00:00:01 - SDL Info (0): SDL found matching display 0 +00:00:01 - Qt Info: Server GPU: "" +00:00:01 - Qt Info: Server GFE version: "3.23.0.74" +00:00:01 - SDL Info (0): Video bitrate: 30000 kbps +00:00:01 - SDL Info (0): Audio channel count: 2 +00:00:01 - SDL Info (0): Audio channel mask: 3 +00:00:02 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaaefd8db30] The input looks like it is Annex B already +mpp[52715]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaaf01e0290] The input looks like it is Annex B already +mpp[52715]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:03 - Qt Info: Processing new PC "DESKTOP-Q072ICM.local." from mDNS with local address "192.168.1.70:47989" +00:00:03 - Qt Info: Executing request: "http://192.168.1.70:47989/serverinfo?uniqueid=0123456789ABCDEF&uuid=bde19ca4ac6f4527a834eac086ade60d" +00:00:03 - Qt Info: Executing request: "https://192.168.1.70:47984/serverinfo?uniqueid=95da1945b8736669&uuid=29837995715346aea2f683aa39046423" +00:00:05 - Qt Info: Latest supported GFE server: "99.99.99.99" +00:00:06 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:06 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:06 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=910b4365f6664f5a894c7e3a58542e6c&appid=881448767&mode=2560x1440x30&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=1&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:06 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:06 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:06 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:06 - Qt Info: Interface MTU: 1500 +00:00:06 - SDL Info (0): RTSP port: 48010 +00:00:06 - SDL Info (0): Initializing platform... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Resolving host name... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing audio stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting RTSP handshake... +00:00:06 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:06 - SDL Info (0): Audio port: 48000 +00:00:06 - SDL Info (0): Video port: 47998 +00:00:06 - SDL Info (0): Control port: 47999 +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing video stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting video stream... +00:00:07 - SDL Info (0): Video stream is 2560x1440x30 (format 0x1) +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting audio stream... +00:00:07 - Qt Info: Gamepad mappings are up to date +00:00:07 - SDL Info (0): Received first video packet after 0 ms +00:00:07 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): SDL audio driver: pulseaudio +00:00:07 - SDL Info (0): Audio stream has 2 channels +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Qt UI screen is at (0,0) +00:00:07 - SDL Info (0): SDL found matching display 0 +00:00:07 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:07 - SDL Info (0): Video decode unit queue overflow +00:00:07 - SDL Info (0): IDR frame request sent +00:00:07 - SDL Info (0): Waiting for IDR frame +00:00:07 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:07 - SDL Info (0): V-sync enabled +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaaefcb7f90] The input looks like it is Annex B already +mpp[52715]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Picked up an existing RKMPP hardware device +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Received a frame +00:00:08 - SDL Info (0): Not reusing test decoder for h264_rkmpp +00:00:08 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:08 - SDL Info (0): GPU driver: rockchip +00:00:08 - SDL Info (0): Direct rendering via DRM is disabled +00:00:08 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Frame pacing: target 60 Hz with 30 FPS stream +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaaefca77e0] The input looks like it is Annex B already +mpp[52715]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Picked up an existing RKMPP hardware device +00:00:08 - SDL Info (0): Using H.264 SPS fixup +00:00:08 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:08 - SDL Info (0): Renderer 'EGL/GLES' with 'DRM' backend chosen +00:00:08 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:08 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Wrote 53656 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaaef6fce10] Received a frame +00:00:08 - SDL Info (0): EGLRenderer: EGLImage pixel format: 179 +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_opaque.frag" at ":/data/egl_opaque.frag" +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_overlay.frag" at ":/data/egl_overlay.frag" +00:02:26 - SDL Info (0): Raising 3 keys +00:02:26 - SDL Info (0): Detected quit key combo +00:02:26 - SDL Info (0): Quit event received +00:02:26 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 24.21 FPS +Decoding frame rate: 24.21 FPS +Rendering frame rate: 23.29 FPS +Host processing latency min/max/average: 24.8/86.1/46.2 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 3.02% +Average network latency: 9 ms (variance: 6 ms) +Average decoding time: 8.24 ms +Average frame queue delay: 31.95 ms +Average rendering time (including monitor V-sync latency): 4.19 ms +00:02:26 - SDL Info (0): Stopping input stream... +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Stopping audio stream... +00:02:26 - SDL Info (0): No audio traffic was ever received from the host! +00:02:26 - SDL Info (0): ENet wait interrupted +00:02:26 - SDL Info (0): Control stream connection failed: 4 +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Stopping video stream... +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Stopping control stream... +00:02:26 - SDL Info (0): ENet peer acknowledged disconnection +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Cleaning up input stream... +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Cleaning up video stream... +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Cleaning up control stream... +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Cleaning up audio stream... +00:02:26 - SDL Info (0): done +00:02:26 - SDL Info (0): Cleaning up platform... +00:02:26 - SDL Info (0): done +00:02:26 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:02:26 - SDL Info (0): Loaded 436 new gamepad mappings diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-30-vsync-no-pacing.txt b/docs/evidence/moonlight-20261102/rkmpp-egl-30-vsync-no-pacing.txt new file mode 100644 index 0000000..a0df590 --- /dev/null +++ b/docs/evidence/moonlight-20261102/rkmpp-egl-30-vsync-no-pacing.txt @@ -0,0 +1,240 @@ +00:00:00 - SDL Info (0): Compiled with SDL 2.30.0 +00:00:00 - SDL Info (0): Running with SDL 2.30.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:02 - SDL Info (0): Qt UI screen is at (0,0) +00:00:02 - SDL Info (0): SDL found matching display 0 +00:00:02 - Qt Info: Server GPU: "" +00:00:02 - Qt Info: Server GFE version: "3.23.0.74" +00:00:02 - SDL Info (0): Video bitrate: 30000 kbps +00:00:02 - SDL Info (0): Audio channel count: 2 +00:00:02 - SDL Info (0): Audio channel mask: 3 +00:00:02 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:02 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaac0ba40c0] The input looks like it is Annex B already +mpp[56346]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264_mp4toannexb @ 0xaaaac0c0e890] The input looks like it is Annex B already +mpp[56346]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Picked up an existing RKMPP hardware device +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Noticed an info change +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:02 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Received a frame +00:00:02 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:03 - Qt Info: Processing new PC "DESKTOP-Q072ICM.local." from mDNS with local address "192.168.1.70:47989" +00:00:03 - Qt Info: Executing request: "http://192.168.1.70:47989/serverinfo?uniqueid=0123456789ABCDEF&uuid=60ec9920646340b7a621736f9ae3ef0f" +00:00:03 - Qt Info: Executing request: "https://192.168.1.70:47984/serverinfo?uniqueid=95da1945b8736669&uuid=9ff5b4f5a4e34249a2e35f84ea1114b4" +00:00:05 - Qt Info: Latest supported GFE server: "99.99.99.99" +00:00:06 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:06 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:06 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=1217e2a33f3e46c282856a4e5d12ee0d&appid=881448767&mode=2560x1440x30&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=1&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:06 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:06 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:06 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:06 - Qt Info: Interface MTU: 1500 +00:00:06 - SDL Info (0): RTSP port: 48010 +00:00:06 - SDL Info (0): Initializing platform... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Resolving host name... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing audio stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting RTSP handshake... +00:00:07 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:07 - SDL Info (0): Audio port: 48000 +00:00:07 - SDL Info (0): Video port: 47998 +00:00:07 - SDL Info (0): Control port: 47999 +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing video stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Initializing input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting control stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting video stream... +00:00:07 - SDL Info (0): Video stream is 2560x1440x30 (format 0x1) +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting audio stream... +00:00:07 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:07 - SDL Info (0): SDL audio driver: pulseaudio +00:00:07 - SDL Info (0): Audio stream has 2 channels +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Starting input stream... +00:00:07 - SDL Info (0): done +00:00:07 - SDL Info (0): Received first video packet after 0 ms +00:00:07 - SDL Info (0): Qt UI screen is at (0,0) +00:00:07 - SDL Info (0): SDL found matching display 0 +00:00:07 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:07 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:07 - SDL Info (0): V-sync enabled +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:07 - SDL Info (0): Video decode unit queue overflow +00:00:07 - SDL Info (0): IDR frame request sent +00:00:07 - SDL Info (0): Waiting for IDR frame +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +mpp[56346]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaac1032530] The input looks like it is Annex B already +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Picked up an existing RKMPP hardware device +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:08 - SDL Info (0): Video decode unit queue overflow +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - SDL Info (0): Waiting for IDR frame +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 390 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Received a frame +00:00:08 - SDL Info (0): Not reusing test decoder for h264_rkmpp +00:00:08 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:08 - SDL Info (0): GPU driver: rockchip +00:00:08 - SDL Info (0): Direct rendering via DRM is disabled +00:00:08 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:08 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:08 - SDL Info (0): Frame pacing disabled: target 60 Hz with 30 FPS stream +00:00:08 - SDL Info (0): Using DRM renderer +00:00:08 - FFmpeg: [h264_mp4toannexb @ 0xaaaac0ee99b0] The input looks like it is Annex B already +mpp[56346]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Picked up an existing RKMPP hardware device +00:00:08 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:08 - SDL Info (0): Using H.264 SPS fixup +00:00:08 - SDL Info (0): Renderer 'EGL/GLES' with 'DRM' backend chosen +00:00:08 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:08 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 45400 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 15128 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 15128 bytes to decoder +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Noticed an info change +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Wrote 15128 bytes to decoder +00:00:08 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:08 - SDL Info (0): IDR frame request sent +00:00:08 - FFmpeg: [h264_rkmpp @ 0xaaaac0530820] Received a frame +00:00:08 - SDL Info (0): EGLRenderer: EGLImage pixel format: 179 +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_opaque.frag" at ":/data/egl_opaque.frag" +00:00:08 - Qt Info: Found "egl.vert" at ":/data/egl.vert" +00:00:08 - Qt Info: Found "egl_overlay.frag" at ":/data/egl_overlay.frag" +00:02:33 - SDL Info (0): Raising 3 keys +00:02:33 - SDL Info (0): Detected quit key combo +00:02:33 - SDL Info (0): Quit event received +00:02:33 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 23.26 FPS +Decoding frame rate: 23.26 FPS +Rendering frame rate: 22.63 FPS +Host processing latency min/max/average: 34.4/110.6/47.5 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 2.23% +Average network latency: 13 ms (variance: 8 ms) +Average decoding time: 8.28 ms +Average frame queue delay: 28.93 ms +Average rendering time (including monitor V-sync latency): 4.14 ms +00:02:34 - Qt Info: Gamepad mappings are up to date +00:02:34 - SDL Info (0): Stopping input stream... +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Stopping audio stream... +00:02:34 - SDL Info (0): No audio traffic was ever received from the host! +00:02:34 - SDL Info (0): ENet wait interrupted +00:02:34 - SDL Info (0): Control stream connection failed: 4 +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Stopping video stream... +00:02:34 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:02:34 - SDL Info (0): Loaded 436 new gamepad mappings +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Stopping control stream... +00:02:34 - SDL Info (0): ENet peer acknowledged disconnection +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Cleaning up input stream... +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Cleaning up video stream... +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Cleaning up control stream... +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Cleaning up audio stream... +00:02:34 - SDL Info (0): done +00:02:34 - SDL Info (0): Cleaning up platform... +00:02:34 - SDL Info (0): done diff --git a/docs/evidence/moonlight-20261102/rkmpp-egl-motion.png b/docs/evidence/moonlight-20261102/rkmpp-egl-motion.png new file mode 100644 index 0000000..edae716 Binary files /dev/null and b/docs/evidence/moonlight-20261102/rkmpp-egl-motion.png differ diff --git a/docs/evidence/moonlight-20261102/rkmpp-first.png b/docs/evidence/moonlight-20261102/rkmpp-first.png new file mode 100644 index 0000000..aedbfdf Binary files /dev/null and b/docs/evidence/moonlight-20261102/rkmpp-first.png differ diff --git a/docs/evidence/moonlight-20261102/rkmpp-stale-sdl.txt b/docs/evidence/moonlight-20261102/rkmpp-stale-sdl.txt new file mode 100644 index 0000000..11d209f --- /dev/null +++ b/docs/evidence/moonlight-20261102/rkmpp-stale-sdl.txt @@ -0,0 +1,300 @@ +00:00:00 - SDL Info (0): Compiled with SDL 2.30.0 +00:00:00 - SDL Info (0): Running with SDL 2.30.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:01 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-error_outline-24px.svg: Unsupported image format +00:00:01 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-help_outline-24px.svg: Unsupported image format +00:00:01 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-error_outline-24px.svg: Unsupported image format +00:00:01 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-error_outline-24px.svg: Unsupported image format +00:00:01 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-error_outline-24px.svg: Unsupported image format +00:00:01 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-error_outline-24px.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/ic_videogame_asset_white_48px.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/update.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/ic_add_to_queue_white_48px.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/discord.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/arrow_left.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/settings.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/question_mark.svg: Unsupported image format +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:02 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-help_outline-24px.svg: Unsupported image format +00:00:02 - Qt Warning: qrc:/gui/NavigableMessageDialog.qml:32:9: QML Image: Error decoding: qrc:/res/baseline-error_outline-24px.svg: Unsupported image format +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:02 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:02 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:02 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:02 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:02 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/arrow_left.svg: Unsupported image format +00:00:02 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/arrow_left.svg: Unsupported image format +00:00:02 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/arrow_left.svg: Unsupported image format +00:00:02 - Qt Warning: file:///home/armbian/vdi-build/rkmpp-experiment/deps/usr/lib/aarch64-linux-gnu/qt5/qml/QtQuick/Controls.2/Material/ToolButton.qml: QML IconImage: Error decoding: qrc:/res/arrow_left.svg: Unsupported image format +00:00:02 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:03 - SDL Info (0): Qt UI screen is at (0,0) +00:00:03 - SDL Info (0): SDL found matching display 0 +00:00:03 - Qt Info: Server GPU: "" +00:00:03 - Qt Info: Server GFE version: "3.23.0.74" +00:00:03 - SDL Info (0): Video bitrate: 30000 kbps +00:00:03 - SDL Info (0): Audio channel count: 2 +00:00:03 - SDL Info (0): Audio channel mask: 3 +00:00:03 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:03 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:03 - SDL Info (0): SDL audio driver: pulseaudio +00:00:03 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:03 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:03 - SDL Info (0): V-sync disabled +00:00:03 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:03 - SDL Info (0): GPU driver: rockchip +00:00:03 - SDL Info (0): Direct rendering via DRM is disabled +00:00:03 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:03 - SDL Info (0): GPU driver: rockchip +00:00:03 - SDL Info (0): Direct rendering via DRM is disabled +00:00:03 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:03 - SDL Info (0): GPU driver: rockchip +00:00:03 - SDL Info (0): Direct rendering via DRM is disabled +00:00:03 - SDL Info (0): Using DRM renderer +00:00:03 - FFmpeg: [h264_mp4toannexb @ 0xaaaacc36b700] The input looks like it is Annex B already +mpp[44667]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Picked up an existing RKMPP hardware device +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Noticed an info change +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Received a frame +00:00:03 - SDL Info (0): Selected hwframe->swframe format: 23 (mapping: yes) +00:00:03 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:03 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:03 - SDL Info (0): V-sync disabled +00:00:03 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:03 - SDL Info (0): GPU driver: rockchip +00:00:03 - SDL Info (0): Direct rendering via DRM is disabled +00:00:03 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:03 - SDL Info (0): GPU driver: rockchip +00:00:03 - SDL Info (0): Direct rendering via DRM is disabled +00:00:03 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:03 - SDL Info (0): GPU driver: rockchip +00:00:03 - SDL Info (0): Direct rendering via DRM is disabled +00:00:03 - SDL Info (0): Using DRM renderer +00:00:03 - FFmpeg: [h264_mp4toannexb @ 0xaaaacc3369a0] The input looks like it is Annex B already +mpp[44667]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Picked up an existing RKMPP hardware device +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Noticed an info change +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:03 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Received a frame +00:00:03 - SDL Info (0): Selected hwframe->swframe format: 23 (mapping: yes) +00:00:03 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:03 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:03 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:05 - Qt Info: Gamepad mappings are up to date +00:00:06 - Qt Info: Processing new PC "DESKTOP-Q072ICM.local." from mDNS with local address "192.168.1.70:47989" +00:00:06 - Qt Info: Executing request: "http://192.168.1.70:47989/serverinfo?uniqueid=0123456789ABCDEF&uuid=38035ae211ee431ab78b9142e0450ce3" +00:00:06 - Qt Info: Executing request: "https://192.168.1.70:47984/serverinfo?uniqueid=95da1945b8736669&uuid=3f6cd9485a3b4e4c92fe07f7076ea0cf" +00:00:08 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:08 - SDL Info (0): Loaded 436 new gamepad mappings +00:00:08 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=21acc81568604fefac1fa259037e9792&appid=881448767&mode=2560x1440x20&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=1&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:08 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:08 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:08 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:08 - Qt Info: Interface MTU: 1500 +00:00:08 - SDL Info (0): RTSP port: 48010 +00:00:08 - SDL Info (0): Initializing platform... +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Resolving host name... +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Initializing audio stream... +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Starting RTSP handshake... +00:00:08 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:08 - SDL Info (0): Audio port: 48000 +00:00:08 - SDL Info (0): Video port: 47998 +00:00:08 - SDL Info (0): Control port: 47999 +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Initializing control stream... +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Initializing video stream... +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Initializing input stream... +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Starting control stream... +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Starting video stream... +00:00:08 - SDL Info (0): Video stream is 2560x1440x20 (format 0x1) +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Starting audio stream... +00:00:08 - SDL Info (0): Received first video packet after 0 ms +00:00:08 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:08 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:08 - SDL Info (0): SDL audio driver: pulseaudio +00:00:08 - SDL Info (0): Audio stream has 2 channels +00:00:08 - SDL Info (0): done +00:00:08 - SDL Info (0): Starting input stream... +00:00:08 - SDL Info (0): done +00:00:09 - SDL Info (0): Qt UI screen is at (0,0) +00:00:09 - SDL Info (0): SDL found matching display 0 +00:00:09 - SDL Info (0): Found display mode with video resolution: 2560x1440x60 +00:00:09 - SDL Info (0): Video decode unit queue overflow +00:00:09 - SDL Info (0): IDR frame request sent +00:00:09 - SDL Info (0): Waiting for IDR frame +00:00:11 - SDL Info (0): Video decode unit queue overflow +00:00:11 - SDL Info (0): IDR frame request sent +00:00:11 - SDL Info (0): Waiting for IDR frame +00:00:13 - SDL Info (0): Video decode unit queue overflow +00:00:13 - SDL Info (0): IDR frame request sent +00:00:13 - SDL Info (0): Waiting for IDR frame +00:00:14 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:14 - SDL Info (0): V-sync enabled +00:00:14 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:14 - SDL Info (0): GPU driver: rockchip +00:00:14 - SDL Info (0): Direct rendering via DRM is disabled +00:00:14 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:14 - SDL Info (0): GPU driver: rockchip +00:00:14 - SDL Info (0): Direct rendering via DRM is disabled +00:00:14 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:14 - SDL Info (0): GPU driver: rockchip +00:00:14 - SDL Info (0): Direct rendering via DRM is disabled +00:00:15 - SDL Info (0): Video decode unit queue overflow +00:00:15 - SDL Info (0): IDR frame request sent +00:00:15 - SDL Info (0): Waiting for IDR frame +00:00:15 - SDL Info (0): Using DRM renderer +00:00:15 - FFmpeg: [h264_mp4toannexb @ 0xaaaacc468440] The input looks like it is Annex B already +mpp[44667]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:15 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Picked up an existing RKMPP hardware device +00:00:15 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:15 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:15 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Noticed an info change +00:00:15 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:15 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Configured with size: 1280x720 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 390 bytes to decoder +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Received a frame +00:00:16 - SDL Info (0): Selected hwframe->swframe format: 23 (mapping: yes) +00:00:16 - SDL Info (0): Not reusing test decoder for h264_rkmpp +00:00:16 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:16 - SDL Info (0): GPU driver: rockchip +00:00:16 - SDL Info (0): Direct rendering via DRM is disabled +00:00:16 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:16 - SDL Info (0): GPU driver: rockchip +00:00:16 - SDL Info (0): Direct rendering via DRM is disabled +mpp[44667]: mpp_info: mpp version: 7916c3c author: Xiaoxu Chen 2026-10-28 fix[utils]: Fix AVC cu_map allocation stride +00:00:16 - SDL Info (0): Frame pacing: target 60 Hz with 20 FPS stream +00:00:16 - SDL Info (0): SDL renderer backend: opengles2 +00:00:16 - SDL Info (0): SDL renderer backend requires main thread rendering +00:00:16 - SDL Info (0): Using DRM renderer +00:00:16 - FFmpeg: [h264_mp4toannexb @ 0xaaaaccea90d0] The input looks like it is Annex B already +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Picked up an existing RKMPP hardware device +00:00:16 - SDL Info (0): Using H.264 SPS fixup +00:00:16 - SDL Info (0): Renderer 'SDL' with 'DRM' backend chosen +00:00:16 - SDL Warn (0): Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp +00:00:16 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 184376 bytes to decoder +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Noticed an info change +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Decoder options: deint=true afbc=0 fast_parse=true buf_mode=0 +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Configured with size: 2560x1440 | pix_fmt: drm_prime | sw_pix_fmt: nv12 +00:00:16 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:16 - SDL Info (0): IDR frame request sent +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 77048 bytes to decoder +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 101816 bytes to decoder +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Wrote 129336 bytes to decoder +00:00:16 - FFmpeg: [h264_rkmpp @ 0xaaaacb767aa0] Received a frame +00:00:16 - SDL Info (0): Selected hwframe->swframe format: 23 (mapping: yes) +00:02:40 - SDL Info (0): Raising 3 keys +00:02:40 - SDL Info (0): Detected quit key combo +00:02:40 - SDL Info (0): Quit event received +00:02:40 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 8.72 FPS +Decoding frame rate: 8.72 FPS +Rendering frame rate: 8.61 FPS +Host processing latency min/max/average: 25.8/35.9/30.7 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.24% +Average network latency: 2 ms (variance: 0 ms) +Average decoding time: 16.89 ms +Average frame queue delay: 1.07 ms +Average rendering time (including monitor V-sync latency): 44.80 ms +00:02:40 - SDL Info (0): Stopping input stream... +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Stopping audio stream... +00:02:40 - SDL Info (0): No audio traffic was ever received from the host! +00:02:40 - SDL Info (0): ENet wait interrupted +00:02:40 - SDL Info (0): Control stream connection failed: 4 +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Stopping video stream... +00:02:40 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:02:40 - SDL Info (0): Loaded 436 new gamepad mappings +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Stopping control stream... +00:02:40 - SDL Info (0): ENet peer acknowledged disconnection +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Cleaning up input stream... +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Cleaning up video stream... +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Cleaning up control stream... +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Cleaning up audio stream... +00:02:40 - SDL Info (0): done +00:02:40 - SDL Info (0): Cleaning up platform... +00:02:40 - SDL Info (0): done diff --git a/docs/evidence/moonlight-20261102/smooth-motion.png b/docs/evidence/moonlight-20261102/smooth-motion.png new file mode 100644 index 0000000..4412062 Binary files /dev/null and b/docs/evidence/moonlight-20261102/smooth-motion.png differ diff --git a/docs/evidence/moonlight-20261102/smooth-software.txt b/docs/evidence/moonlight-20261102/smooth-software.txt new file mode 100644 index 0000000..daaab69 --- /dev/null +++ b/docs/evidence/moonlight-20261102/smooth-software.txt @@ -0,0 +1,250 @@ +00:00:00 - SDL Info (0): Compiled with SDL 2.33.0 +00:00:00 - SDL Info (0): Running with SDL 2.33.0 +00:00:00 - Qt Info: Successfully loaded translation for "zh_CN" +00:00:00 - Qt Info: Loaded unique ID from settings: "95da1945b8736669" +00:00:00 - Qt Warning: qrc:/gui/main.qml:13:1: QML ApplicationWindow: ToolTip must be attached to an Item +00:00:01 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:01 - SDL Info (0): Loaded 435 new gamepad mappings +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 192.168.1.70:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 255.255.255.255:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via 60.21.96.193:48010 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:9 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47009 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47998 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:47999 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48000 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48002 +00:00:01 - Qt Info: Sent WoL packet to DESKTOP-Q072ICM via ff02::1%wlan0:48010 +00:00:01 - Qt Debug: Current Moonlight version: "6.2.0" +00:00:01 - Qt Info: "DESKTOP-Q072ICM" is now online at "192.168.1.70:47989" +00:00:01 - Qt Info: Found "ModeSeven.ttf" at ":/data/ModeSeven.ttf" +00:00:01 - Qt Info: Discovered mDNS host: "DESKTOP-Q072ICM.local." +00:00:01 - SDL Info (0): Qt UI screen is at (0,0) +00:00:01 - SDL Info (0): SDL found matching display 0 +00:00:01 - Qt Info: Server GPU: "" +00:00:01 - Qt Info: Server GFE version: "3.23.0.74" +00:00:01 - SDL Info (0): Video bitrate: 30000 kbps +00:00:01 - SDL Info (0): Audio channel count: 2 +00:00:01 - SDL Info (0): Audio channel mask: 3 +00:00:01 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:01 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:01 - SDL Info (0): SDL audio driver: pulseaudio +00:00:02 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:02 - SDL Info (0): Loaded 435 new gamepad mappings +00:00:02 - SDL Info (0): V-sync disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:02 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Decoding VUI +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format drm_prime chosen by get_format(). +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Invalid setup for format drm_prime: missing configuration. +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format drm_prime not usable, retrying get_format() without it. +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format yuv420p chosen by get_format(). +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:02 - SDL Warn (0): EGLImage export requires hardware-backed frames +00:00:02 - SDL Warn (0): Backend failed to export EGL image for test frame +00:00:02 - SDL Warn (0): Test decode failed (testRenderFrame) +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:02 - SDL Info (0): GPU driver: rockchip +00:00:02 - SDL Info (0): Direct rendering via DRM is disabled +00:00:02 - SDL Info (0): Using DRM renderer +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Decoding VUI +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format yuv420p chosen by get_format(). +00:00:02 - FFmpeg: [h264 @ 0xaaaabb28ac40] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:02 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:02 - SDL Info (0): Encoder configured for 4 slices per frame +00:00:02 - Qt Warning: 启用远程桌面鼠标模式可能会导致游戏出现问题。 +00:00:02 - Qt Warning: 你选择的用于强制软件解码的设置可能会导致流式传输性能不佳。 +00:00:03 - Qt Info: Processing new PC "DESKTOP-Q072ICM.local." from mDNS with local address "192.168.1.70:47989" +00:00:03 - Qt Info: Executing request: "http://192.168.1.70:47989/serverinfo?uniqueid=0123456789ABCDEF&uuid=6962458dceb442b4b6bd8028e70a3b36" +00:00:03 - Qt Info: Executing request: "https://192.168.1.70:47984/serverinfo?uniqueid=95da1945b8736669&uuid=24015164fe774f5790b5e26dcbdac0a3" +00:00:04 - Qt Info: Gamepad mappings are up to date +00:00:05 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:00:05 - SDL Info (0): Loaded 435 new gamepad mappings +00:00:05 - Qt Info: Executing request: "https://192.168.1.70:47984/resume?uniqueid=95da1945b8736669&uuid=49737f9390044dc28228a93b794837cb&appid=881448767&mode=2560x1440x20&additionalStates=1&sops=1&rikey=REDACTED&rikeyid=REDACTED&localAudioPlayMode=0&surroundAudioInfo=196610&remoteControllersBitmap=0&gcmap=0&gcpersist=0&corever=1" +00:00:06 - Qt Info: Launch response: "\nrtspenc://192.168.1.70:480101" +00:00:06 - Qt Info: Found matching interface: "wlan0" "88:00:33:77:30:B6" QFlags(IsUp|IsRunning|CanBroadcast|CanMulticast) +00:00:06 - Qt Info: Interface Type: QNetworkInterface::Wifi +00:00:06 - Qt Info: Interface MTU: 1500 +00:00:06 - SDL Info (0): RTSP port: 48010 +00:00:06 - SDL Info (0): Initializing platform... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Resolving host name... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing audio stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting RTSP handshake... +00:00:06 - SDL Info (0): Reference frame invalidation is not supported by this host +00:00:06 - SDL Info (0): Audio port: 48000 +00:00:06 - SDL Info (0): Video port: 47998 +00:00:06 - SDL Info (0): Control port: 47999 +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing control stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing video stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Initializing input stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting control stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting video stream... +00:00:06 - SDL Info (0): Video stream is 2560x1440x20 (format 0x1) +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting audio stream... +00:00:06 - SDL Info (0): Desired audio buffer: 720 samples (5760 bytes) +00:00:06 - SDL Info (0): Obtained audio buffer: 720 samples (5760 bytes) +00:00:06 - SDL Info (0): SDL audio driver: pulseaudio +00:00:06 - SDL Info (0): Received first video packet after 0 ms +00:00:06 - SDL Info (0): Audio stream has 2 channels +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Starting input stream... +00:00:06 - SDL Info (0): done +00:00:06 - SDL Info (0): Qt UI screen is at (0,0) +00:00:06 - SDL Info (0): SDL found matching display 0 +00:00:06 - SDL Info (0): Found display mode with desktop resolution: 2560x1440x60 +00:00:06 - SDL Info (0): Recreating renderer for window event: 1 (0 0) +00:00:06 - SDL Info (0): V-sync enabled +00:00:06 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:06 - SDL Info (0): GPU driver: rockchip +00:00:06 - SDL Info (0): Direct rendering via DRM is disabled +00:00:06 - SDL Info (0): DRM backend supports exporting EGLImage +00:00:07 - SDL Info (0): Video decode unit queue overflow +00:00:07 - SDL Info (0): IDR frame request sent +00:00:07 - SDL Info (0): Waiting for IDR frame +00:00:07 - SDL Info (0): Color buffer is: R8G8B8A0 +00:00:07 - SDL Info (0): Frame pacing: target 60 Hz with 20 FPS stream +00:00:07 - SDL Info (0): Using DRM renderer +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Decoding VUI +00:00:07 - SDL Warn (0): Unable to set render thread to high priority: setpriority() failed +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format drm_prime chosen by get_format(). +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Invalid setup for format drm_prime: missing configuration. +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format drm_prime not usable, retrying get_format() without it. +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format yuv420p chosen by get_format(). +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:07 - SDL Warn (0): EGLImage export requires hardware-backed frames +00:00:07 - SDL Warn (0): Backend failed to export EGL image for test frame +00:00:07 - SDL Warn (0): Test decode failed (testRenderFrame) +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): Opened DRM render node: /dev/dri/renderD128 +00:00:07 - SDL Info (0): GPU driver: rockchip +00:00:07 - SDL Info (0): Direct rendering via DRM is disabled +00:00:07 - SDL Info (0): Frame pacing: target 60 Hz with 20 FPS stream +00:00:07 - SDL Info (0): SDL renderer backend: opengles2 +00:00:07 - SDL Info (0): SDL renderer backend requires main thread rendering +00:00:07 - SDL Info (0): Using DRM renderer +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Decoding VUI +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format yuv420p chosen by get_format(). +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Reinit context to 1280x720, pix_fmt: yuv420p +00:00:07 - SDL Info (0): Test decode successful +00:00:07 - SDL Info (0): Using H.264 SPS fixup +00:00:07 - SDL Info (0): Renderer 'SDL' with 'DRM' backend chosen +00:00:07 - SDL Info (0): FFmpeg-based video decoder chosen +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 7(SPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 8(PPS), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] nal_unit_type: 5(IDR), nal_ref_idc: 3 +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Decoding VUI +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Format yuvj420p chosen by get_format(). +00:00:07 - FFmpeg: [h264 @ 0xaaaabb28ac40] Reinit context to 2560x1440, pix_fmt: yuvj420p +00:00:07 - SDL Info (0): Dropping window event during flush: 6 (2560 1440) +00:00:07 - SDL Info (0): IDR frame request sent +00:01:54 - SDL Info (0): Raising 3 keys +00:01:54 - SDL Info (0): Detected quit key combo +00:01:54 - SDL Info (0): Quit event received +00:01:54 - SDL Info (0): +Global video stats +------------------ +Incoming frame rate from network: 17.16 FPS +Decoding frame rate: 17.16 FPS +Rendering frame rate: 17.07 FPS +Host processing latency min/max/average: 24.1/53.8/33.6 ms +Frames dropped by your network connection: 0.00% +Frames dropped due to network jitter: 0.44% +Average network latency: 11 ms (variance: 7 ms) +Average decoding time: 14.07 ms +Average frame queue delay: 0.95 ms +Average rendering time (including monitor V-sync latency): 8.38 ms +00:01:54 - SDL Info (0): Stopping input stream... +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Stopping audio stream... +00:01:54 - SDL Info (0): No audio traffic was ever received from the host! +00:01:54 - SDL Info (0): ENet wait interrupted +00:01:54 - SDL Info (0): Control stream connection failed: 4 +00:01:54 - Qt Info: Found "gamecontrollerdb.txt" at "/home/armbian/.cache/Moonlight Game Streaming Project/Moonlight/gamecontrollerdb.txt" +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Stopping video stream... +00:01:54 - SDL Info (0): Loaded 435 new gamepad mappings +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Stopping control stream... +00:01:54 - SDL Info (0): ENet peer acknowledged disconnection +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Cleaning up input stream... +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Cleaning up video stream... +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Cleaning up control stream... +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Cleaning up audio stream... +00:01:54 - Qt Warning: stream 1 finished with error: "Connection closed" +00:01:54 - SDL Info (0): done +00:01:54 - SDL Info (0): Cleaning up platform... +00:01:54 - SDL Info (0): done diff --git a/docs/gpu-restart-diagnosis-20261102.md b/docs/gpu-restart-diagnosis-20261102.md new file mode 100644 index 0000000..fcd6156 --- /dev/null +++ b/docs/gpu-restart-diagnosis-20261102.md @@ -0,0 +1,49 @@ +# GTX 965M 重启故障诊断(2026-11-02) + +## 结论 + +未找到已验证有效的重启后 NVENC 恢复方案。所有实验配置已恢复,VM100 可继续使用软件编码回退;VM101/102 全程保持运行。 + +此前582.78、DXGI与NVENC确实工作过;此轮故障状态为Windows Code43、nvlddmkm停止、nvidia-smi失败、Sunshine回退libx264。不能以设备管理器显示型号或PnP暂时OK作为恢复成功的依据。 + +## 原始配置与备份 + +宿主机备份目录:`/root/vdi-gpu-recovery-20261102/`,包含100.conf、vfio.conf、ROM及PCI信息。 + +- hostpci0:`0000:01:00.0,pcie=1,rombar=1,romfile=gtx965m-pci.rom` +- args:`-set device.hostpci0.x-pci-sub-vendor-id=0x1558 -set device.hostpci0.x-pci-sub-device-id=0x6557` +- GPU:10de:13d9,子系统1558:6557,总线01仅此设备。 +- 原ROM SHA256:`080d9765337a5ea3bceefb1f632f073e14b09d215ef44b885e2d97cb04d27160` +- `/etc/modprobe.d/vfio.conf`:`options vfio-pci ids=10de:13d9 disable_idle_d3=1`,本轮未修改该文件。 +- Windows快速启动原值HiberbootEnabled=1;本轮临时关闭后无收益,已恢复1。 + +## 实验与结果 + +| 实验 | 结果 | +|---|---| +| 关闭Windows快速启动,VM完整停止/启动 | Code43仍存在 | +| 临时disable_idle_d3=N,再解绑/重绑VFIO | D0 active、runtime_usage=1;无法证明完成电源周期 | +| 干净卸载并重载仅vfio_pci模块,disable_idle_d3=0 | 引用计数1→0;实测runtime suspended、D3cold;启动VM后D0,但Code43/NVML失败仍存在 | +| 原供体ROM校验和修正副本 | Windows Guest Agent未上线,未保留 | +| 本机既有sysfs ROM副本 | Windows启动但Code43/NVML失败 | +| 本机ROM+已确认D3cold电源周期 | 同样失败,未保留 | + +没有写PMCSR、没有刷写显卡固件、没有重启宿主机、没有卸载KVM或VFIO核心模块;仅在VM100停止且无VFIO使用者时重载vfio_pci。最终模块参数已恢复Y,原ROM配置已恢复。 + +## ROM证据与局限 + +1. 当前原始供体ROM为184320字节:传统BIOS镜像110080字节,PCIR ID10de:13d9,镜像和模256为1;第二UEFI镜像74240字节仍为10de:13d8。版本84.04.22.00.11。这些是明确的不一致,但不能认定为唯一故障原因;VM使用SeaBIOS,UEFI ID不匹配不等同于此次启动必然失败。 +2. 既有本机sysfs ROM为110592字节,10de:13d9、校验和0、版本84.04.2B.00.13,字符串含HWDIAG。尚未确认是完整的、适配此板卡的生产版固件;不能只凭校验和正常就当作可靠替代。 +3. 两份ROM均未刷入硬件。测试副本留在宿主机,当前VM不使用它们。 + +## 下一步所需条件 + +- 优先由运维/设备厂商提供该CLEVO板卡的生产版GM204M/13D9 vBIOS及适配依据,保留原件后再验证。 +- 如需要宿主机冷启动、内核/VFIO升级或平台固件实验,先安排维护窗口,备份全部VM与宿主启动配置;不能影响其他VM而不协调。 +- 当前证据只证明D3cold电源周期可实现,并不证明它能够修复此显卡;不要添加反复复位/重启任务充当永久修复。 + +## 验收标准 + +nvidia-smi成功、DXGI枚举NVIDIA、Sunshine实际选择h264_nvenc、真实串流有编码利用率;随后Windows重启和VM停启后重复通过。2K显示、20FPS配置和正常Windows单光标应同时保留。 + +参考:[Linux PCI](https://docs.kernel.org/PCI/pci.html)、[VFIO](https://docs.kernel.org/driver-api/vfio.html)、[PCI sysfs](https://docs.kernel.org/PCI/sysfs-pci.html)、[Proxmox qm](https://pve.proxmox.com/pve-docs/qm.1.html)。 diff --git a/docs/independent-cursor-design-20261103.md b/docs/independent-cursor-design-20261103.md new file mode 100644 index 0000000..533c0f5 --- /dev/null +++ b/docs/independent-cursor-design-20261103.md @@ -0,0 +1,62 @@ +# 独立光标二次开发设计与接入条件 + +状态:源码核实完成,尚未实现 Sunshine/Moonlight 的独立光标通道,不应标记为已部署。当前保留单个 Windows 远程白色光标。 + +## 已核实的实际路径 + +- 盒子固定 Moonlight Qt 6.2.0,现有 absolute mouse 输入仍经 moonlight-common-c 的可靠输入通道发送。 +- 当前 RKMPP/EGL 路径使用 PacerRender 独立线程;实际诊断会话没有主线程 render 调用,输入优先调度不会改变这个渲染路径。 +- Sunshine `input.cpp` 的 Ctrl+Alt+Shift+N 直接翻转全局 `display_cursor`。`video.cpp` 将其地址传入 display capture。它没有按会话的目标状态、确认或租约,不能作为可靠的独立光标协议。 +- Windows Desktop Duplication 可能返回独立光标形状,也可能返回已含光标的画面;不能假定所有显示设备都能从视频中无损移除光标。 +- Windows 当前没有现成的 Sunshine C++ 构建环境。因此本轮可以构建盒子客户端、验证输入和刷新率,但不能把未构建的服务端补丁当成已完成联调。 + +参考:[Sunshine input.cpp](https://github.com/LizardByte/Sunshine/blob/master/src/input.cpp)、[video.cpp](https://github.com/LizardByte/Sunshine/blob/master/src/video.cpp)、[Windows RAM capture](https://github.com/LizardByte/Sunshine/blob/master/src/platform/windows/display_ram.cpp)、[Microsoft Desktop Duplication 光标说明](https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api)。master 仅用于核实设计,实际修改须先固定现场 Sunshine 版本对应提交。 + +## 实施方式 + +在 Sunshine 与 moonlight-common-c/Moonlight 间扩展现有加密控制通道;不另外开放未经认证的 UDP 光标端口。默认仍使用远程视频光标,仅在双方协商并确认支持后启用。新增消息必须有协议版本、会话标识、显示配置代数及严格的大小校验。 + +| 消息 | 必要内容与作用 | +| --- | --- | +| 能力协商 | 支持形状类型、最大尺寸、显隐/视频帧交接能力;旧客户端保持原行为 | +| 光标形状 | 形状版本、尺寸、热点、像素/掩码格式;形状变化时发送并缓存 | +| 光标状态 | 状态序号、形状版本、显隐、坐标、显示区域及缩放配置代数 | +| 交接请求/确认 | 明确目标模式,携带首个采用该模式的视频帧 ID;不能使用无确认的 toggle | +| 位置校正 | 服务端主动移动与最近输入序号关联;用户持续移动时不能用旧位置反复拉回 | +| 心跳/结束 | 会话租约、显式结束;连接丢失时服务端自动恢复视频光标 | + +光标移动由盒子的本地指针立即显示,形状和显隐仍取自服务端。窗口、按钮响应和内容更新继续走视频,所以独立光标不等于窗口拖动零延迟。 + +## 单光标交接 + +1. 服务端确认当前捕获后端可独立控制光标,客户端收到并成功创建当前形状;否则保持远程模式。 +2. 服务端在明确的视频帧边界停止混合光标,并发出该帧 ID。客户端显示到这个边界后才开启本地光标,避免旧视频仍有光标时出现双指针。 +3. 结束独立模式时反向交接,在开始包含远程光标的视频帧上隐藏本地光标。 +4. 辅助状态超时、形状不支持、捕获设备更换、显示缩放改变、解码器重建、第二路会话接入时,执行确认后的回退。控制连接完全断开且无法继续交接时结束串流,返回选择页。 +5. 不能通过替换 Windows 全局鼠标主题为透明图片实现;不能把普通箭头覆盖在视频光标上。 + +心跳和租约先按 250ms/1000ms 做实验参数,最终值由 Wi-Fi 抖动及故障恢复测试决定。现有全局 display_cursor 还须改为与会话/捕获生命周期一致的状态,防止多个客户端互相覆盖。 + +## Windows 特殊情况 + +- 处理彩色 alpha、单色 AND/XOR 和 masked-color;不支持的形状明确回退。不能把 XOR 像素简单当透明像素。 +- 热点随流画面缩放映射,窗口信箱区域不发送越界输入;切换分辨率后废弃旧代数的光标消息。 +- 服务端隐藏指针、文字选择、拖动缩放、忙碌动画必须正确;锁屏/UAC 桌面切换不能继续显示过期光标。 +- SDL 硬件指针操作留在主线程;网络线程仅提交有界状态,禁止直接调用 SDL_SetCursor/释放正在使用的 cursor。 +- 本地移动与远程主动 warp 分开处理,避免拖动时抖动、回弹;点击仍使用已修复的按钮坐标/输入顺序。 + +## 必须通过的验收 + +| 场景 | 要求 | +| --- | --- | +| 移动/拖动/右键/快速双击 | 单光标、点击准确、无丢失的按键释放 | +| 箭头/文字/调整大小/忙碌/隐藏/自定义 | 形状、热点及显隐正确;不支持时可靠回退 | +| 100%/125%/150% 缩放、分辨率切换 | 无偏移;旧代数消息失效 | +| 辅助消息停止、控制断开、服务器重启 | 无持久双光标或无光标状态 | +| 窗口失焦、鼠标离开、重新捕获 | 不锁死本地系统指针;恢复后坐标正确 | +| 旧服务端/客户端、多会话 | 不误启用;保持原有远程模式 | +| 高速摄像对照 | 分别报告指针移动、点击响应、拖窗内容更新的 P50/P95 | + +## 下一阶段开始条件 + +固定现场 Sunshine 版本及源码提交,在独立构建目录准备可复现 Windows 构建,备份原安装并在测试实例验证后,再修改双方协议和捕获交接。不得为部署光标实验重装当前 Windows、刷 GPU ROM 或中断其他虚拟机。 diff --git a/docs/input-rendering-optimization-20261103.md b/docs/input-rendering-optimization-20261103.md new file mode 100644 index 0000000..1903b51 --- /dev/null +++ b/docs/input-rendering-optimization-20261103.md @@ -0,0 +1,51 @@ +# 鼠标点击与画面跳转优化(2026-11-03) + +本轮针对用户反馈的“鼠标拖动/点击有残影、点击后画面跳转慢”做了输入队列和低延迟呈现优化。已有单白色 Windows 远程光标、显示器开关自动恢复和 RKMPP/EGL 客户端均保留。 + +## 现场根因 + +1. **点击位置顺序会被旧的绝对鼠标移动批次覆盖。** Moonlight 上游为了减少包数,会让一个尚未发送的绝对鼠标包持续吸收后续位置。高速移动后点击时,点击包与位置包在网络队列中的边界不明确,画面中的 Windows 控件可能在旧位置响应。32 次左右交替点击的实测中,修改前收到 32 条点击记录,其中第 1、13 条落在错误的一侧(30/32 正确)。这是延迟体感之外的可重复顺序错误。 +2. **鼠标移动合并会扫描 SDL 队列。** 原逻辑只按 `SDL_MOUSEMOTION` 类型取事件,可能跨过按钮/键盘/窗口事件,把点击后的移动提前合并。修改后只消费紧邻且属于同一鼠标设备/窗口/按键状态的移动,遇到边界立即停止,每次最多处理 64 条,避免输入线程长时间占用。 +3. **低延迟模式的解码帧积压。** 当前服务端仍为软件 `libx264`、约 30 FPS;盒子已使用 `h264_rkmpp` + `drm_prime` + EGL/GLES。修改后的非 VSync pacing 呈现队列可以丢弃尚未呈现的等待帧,只保留最新等待帧,减少画面落后于点击的时间。正在呈现的帧、上一帧延迟释放和解码参考帧不会被释放。 + +## 实现 + +- `terminal/moonlight/patches/0001-vdi-input-and-latest-frame.patch` + - SDL 输入事件按边界合并。 + - 绝对鼠标按钮使用该按钮事件的坐标先入队。 + - `VDI_LATEST_FRAME=1` 且未启用 VSync pacing 时只替换等待呈现帧;默认 wrapper 开启,smooth/pacing 档保持上游行为。 +- `terminal/moonlight/patches/0002-vdi-absolute-click-barrier.patch` + - 每个点击封住前一位置批次,点击后的移动进入新批次。 + - 发送线程只解除自己包的“可吸收新位置”状态,不误清除点击后新批次。 +- `terminal/kiosk-shell/scripts/vdi-moonlight-rkmpp` 默认 `VDI_LATEST_FRAME=1`,仍不创建本地黑光标、不叠加第二个光标。 +- `tools/build-rkmpp-client.sh` 固定 Moonlight/MPP/FFmpeg 提交,构建前检查并幂等应用补丁;出现源码漂移会失败,不强行覆盖。 +- `tools/set-desktop-responsiveness.ps1` 已在 Windows 登录用户会话执行:备份原设置,关闭窗口缩放动画、菜单等待从 400ms 改为 100ms;通过 `SystemParametersInfoW` 设置并读回校验成功,临时执行任务已删除。失败时脚本会自动恢复。 +- 盒子显示守护改读 Xorg 当前配置 `xrandr --current`,保留热插拔恢复,避免每 2 秒重新探测 EDID/显示模式引发 vendor 驱动时钟日志;对应 9 个场景测试通过。 +- 用户明确蓝框发生于盒子登录/选择桌面页:QLabel 继承 QFrame,原 `QFrame:hover` 蓝框会作用于文字。移除该悬停规则,卡片样式限定到对象名,移除登录输入框的蓝色焦点边框。实际悬停截图文字/卡片区域亮蓝色像素从 8,302 降到 0,按钮仍可正常使用。 + +## 自动化与现场测试 + +| 测试 | 结果 | +|---|---| +| SDL 事件边界、64 条上限、触摸鼠标隔离、最新帧 helper | 通过 | +| 绝对输入 1,000 轮“位置→位置→按下→位置→释放→位置” | 通过 | +| AddressSanitizer 版本上述 native 测试 | 通过,无内存错误 | +| 修改前 32 次交替点击 | 30/32 正确,错误序号 1、13 | +| 修改后 32 次交替点击 | 32/32 正确 | +| 修改后真实 RKMPP/EGL 串流 | 日志确认 `VDI latest-frame queue: enabled`、`h264_rkmpp`、2560×1440 DRM PRIME | +| 配对对照串流 | 原版:28.93 接收/28.48 渲染 FPS,队列 3.39ms,解码 10.49ms,呈现 6.84ms;补丁:28.40/28.09 FPS,队列 2.14ms,解码 7.96ms,呈现 6.12ms | +| 会话连续性 | 测试退出后没有遗留测试串流;显示守护 active;盒子仍 2560×1440 | + +对照数据不是严格实验室基准:两段会话的网络、Windows 软件编码负载和页面内容会波动。补丁最明确的收益是点击顺序正确和队列深度下降约 1.25ms;它不能把当前服务端软件编码变成 NVENC,也不能保证端到端零延迟。测试期间出现过一次 Wi-Fi 从 completed 到 disconnected 后自动重连,受影响的数据已单独保留,未作为性能结论。 + +## Windows 桌面跳转动画 + +执行前 Windows 登录用户状态为 `DragFullWindows=1`、`MouseTrails=0`、`MinAnimate=1`、`MenuShowDelay=400`。已将窗口动画改为 0、子菜单等待改为 100ms,API 读回校验成功,保留完整窗口拖动和原远程单光标。原值备份到 `%LOCALAPPDATA%\VDI\desktop-responsiveness-before-20261103.json`,恢复使用同一脚本的 `-Restore`。这些设置只减少 Windows 自身的动画和子菜单等待,不代表网页加载或网络响应变快。API 依据为 [Microsoft SystemParametersInfoW](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow)。 + +## 当前状态与回退 + +补丁客户端已接入默认 wrapper,启用最新帧队列。kiosk 为移除蓝色悬停框做了局部编译、CTest 3/3、原监控器拉起新版页面;未运行完整部署脚本,未重启盒子、VM 或宿主。UI 和 wrapper 备份为 `~/vdi-terminal/backups/input-rendering-20261103-114542/`。补丁源码、测试和构建说明均在 `terminal/moonlight/`,证据在 `docs/evidence/input-latency-20261103/`。 + +如需临时回退客户端,下次启动 wrapper 时设置 `VDI_RKMPP_CLIENT=/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight.before-input-20261103`、`VDI_LATEST_FRAME=0`。旧二进制 SHA256 为 `a3575f86b9ebfd3b9f2a6ae4c73b057b2c8cd489cc44549a8838ff459c5e89dc`,新二进制为 `35de7b1a4ade583823b23b5f2e012307f6d0a3848c8eb97bd6d4de9660afa00f`。回退后重新连接并查看统计;不要直接覆盖运行中的 ELF。 + +下一步为用户真实鼠标拖窗/右键/快速双击体验验收;严格延迟需通过高速摄像或外部触发测输入到屏幕 P50/P95。当前数据不能宣称“无延迟”,只能确认点击排序和客户端排队得到改善。 diff --git a/docs/moonlight-rendering-profile-20261102.md b/docs/moonlight-rendering-profile-20261102.md new file mode 100644 index 0000000..9a19411 --- /dev/null +++ b/docs/moonlight-rendering-profile-20261102.md @@ -0,0 +1,67 @@ +# Moonlight 客户端渲染 profile + +## 目的 + +统一 `kiosk-shell` 产品入口与现场调试入口的 Moonlight 呈现参数,避免测试时只改了 +`vdi-moonlight` 脚本而正式页面仍使用另一套 VSync、Frame Pacing 和解码器配置。 + +代码默认 profile 是 `smooth`;RK3528现场在硬解与EGL验证通过后,通过render.conf +选择了 `low-latency` 和2K30。软件基线和其他未验证设备仍使用smooth。 +不把关闭同步当成通用修复,现场对照与风险见 `rendering-execution-20261102.md`。 + +## 产品入口环境变量 + +这些变量由 `kiosk-shell` 和 `vdi-moonlight` 调试启动器读取,重启串流进程后生效: + +```text +VDI_STREAM_PROFILE=smooth # 默认,VSync + Frame Pacing +VDI_STREAM_PROFILE=low-latency # 关闭 VSync + Frame Pacing,减少呈现等待 +VDI_STREAM_PROFILE=diagnostic # smooth + Moonlight 性能叠图 +VDI_STREAM_STATS=1 # 独立开启叠图,可同时比较smooth和low-latency +VDI_MOONLIGHT_DECODER=software # 系统客户端默认;RKMPP现场配置使用 hardware +VDI_MOONLIGHT_CODEC=H.264 # 当前默认,先不切换 HEVC/AV1 +VDI_MOONLIGHT_PROGRAM=... # 可指定独立构建的Moonlight启动器,不覆盖系统二进制 +VDI_SDL_FORCE_EGL=1 # 仅现场验证 SDL X11 EGL 时设置 +VDI_SDL_VIDEODRIVER=wayland # 仅实验;正式入口的X11窗口激活逻辑尚需适配 +``` + +每次会话启动时,最终 profile、解码器、编解码器、同步开关、SDL renderer 和完整参数 +会写入 `~/vdi-terminal/logs/moonlight-YYYYMMDD.log` 的 `[VDI launch]` 段落。 + +## A/B 顺序 + +1. 先恢复并确认 Sunshine 使用 `h264_nvenc`,否则客户端 A/B 不能说明服务端问题已经解决。 +2. 1080p60:比较 `smooth` 与 `low-latency`,两组都设置 `VDI_STREAM_STATS=1` 收集 queue/render/jitter。 +3. RK3528 硬解与 EGL/GLES 现场路径已验证;后续仍需单独完成 2K60 验收,不能由 2K30 结果推断。 +4. 任何 profile 都必须保留 `--absolute-mouse`;当前已解决的双光标行为不应回退。 + +## GPU 重启故障边界 + +`gpu-restart-diagnosis-20261102.md` 已确认当前没有经过验证的重启后软件恢复办法。profile +只改变客户端呈现,不会修复 Windows Code 43、NVML 失败或 Sunshine 回退 `libx264`。 +GPU 修复仍需设备厂商提供匹配的 GM204M/13D9 生产版 vBIOS,并在维护窗口内按验收标准 +重复验证 `nvidia-smi`、DXGI、`h264_nvenc` 和 Windows/VM 重启后的持久性。 + +## RK3528现场配置与回退 + +`~/.config/vdi-terminal/render.conf` 当前内容: + +```ini +profile=low-latency +stats=0 +program=/home/armbian/vdi-terminal/bin/vdi-moonlight-rkmpp +decoder=hardware +codec=H.264 +``` + +Windows桌面卡当前请求2560x1440@30、30Mbps,实际持续动画约23FPS; +虚拟屏仍为30Hz。私有构建位于 `~/vdi-build/rkmpp-experiment`,不要删除该目录。 +目前是用户目录内的POC部署,尚未打包为完整OTA镜像。 + +如现场发现关闭同步导致明显撕裂,可先将profile改为smooth,并将桌面卡FPS改回20。 +如需彻底退回系统客户端,退出串流后将render.conf改名保存,并恢复 +`desktops.before-rkmpp-20261102.json` 为desktops.json,然后重新启动kiosk受管栈。 +系统 `/usr/bin/moonlight-qt` 未被覆盖,不需要卸载MPP或替换系统FFmpeg。 + +设备访问规则是 `/etc/udev/rules.d/70-vdi-rockchip-video.rules`,限定video组0660; +部署工具会安装并重载规则。udev匹配已验证,本轮未重启盒子做重启后串流复验。 diff --git a/docs/mouse-optimization-plan-20261103.md b/docs/mouse-optimization-plan-20261103.md new file mode 100644 index 0000000..4e05d11 --- /dev/null +++ b/docs/mouse-optimization-plan-20261103.md @@ -0,0 +1,33 @@ +# 鼠标与拖窗优化实施计划及结果(2026-11-03) + +## 目标与边界 + +降低鼠标输入等待和画面更新延迟,保留单个 Windows 白色光标、正确点击位置、完整窗口拖动及现有显示器恢复功能。用实测决定是否启用,不能将帧间隔、客户端统计或本地光标响应称为端到端零延迟。 + +现场初检:盒子实际启动 2560×1440 / 30 FPS / RKMPP / EGL;Windows GTX 965M 仍 Code 43,Sunshine 实际 libx264,静态画面最低目标约 15 FPS。没有可直接构建 Sunshine 的现成 Windows C++ 工具链。 + +## 执行顺序 + +1. **记录基线。** 保存当前配置、二进制校验及串流日志,确认 GPU、虚拟屏刷新率、网络和解码路径。静态画面重复帧不能作为动态画面性能基准。 +2. **验证刷新率整条链路。** 用临时测试窗口对比虚拟屏/串流 30 与 60 FPS;修改必须支持只读查询、枚举、测试、临时应用和恢复。只有动态画面实测改善且解码/丢帧可接受,才更改默认配置;否则恢复 2K30。 +3. **改进 Moonlight 输入调度。** 检查主线程呈现阻塞输入的问题。允许帧呈现前先处理紧邻的有限数量输入,保持输入间原始顺序、窗口/焦点边界及工作量上限;增加可关闭的输入队列年龄与呈现阻塞统计。编译、自动化边界测试及真实串流对照后决定是否启用。 +4. **核实独立光标的落地条件。** 必须同步光标形状/热点/显隐、单光标交接、缩放及重连回退。Sunshine 现有 Ctrl+Alt+Shift+N 是全局开关,没有按会话确认,不能据此可靠实现断线回退;不能只叠加本地箭头后宣称完成。先形成与实际源码相符的协议和验收要求,具备服务端构建条件后才能集成双方修改并验收。 +5. **交付与现场恢复。** 移除临时测试任务/窗口,恢复正常云桌面连接;记录实际部署状态、测试结果、回退命令和未完成项。 + +## 验收 + +- 输入:快速交替点击无错位;双击、右键、拖动中的按下/释放顺序保持正确;失焦及退出快捷键正常。 +- 调度:窗口和焦点事件不能越过;高频鼠标不能无限拖延视频;禁用新开关可恢复旧调度。 +- 视频:相同动态测试源、相同分辨率/码率下记录接收/渲染 FPS、主机处理、解码、排队、呈现及网络丢帧;避免用空闲画面的平均 FPS 下结论。 +- 光标:只有一个指针;文字/缩放/忙碌形状、显隐正确;断线/辅助通道故障不能无光标或双光标。 +- 严格输入到屏幕延迟留待高速摄像/外部触发验证,客户端软件统计仅衡量其中一段。 + +## GPU 前置条件 + +本轮不重启宿主、不刷 ROM、不动 VM101/102。Code 43 仍需按现有运维方案排查,修复后再验收实际 NVIDIA D3D11 和 NVENC;不能只设置 encoder=nvenc 就认定修复。 + +## 执行结果 + +代码修改、自动化测试和现场配对串流对照已完成,结果见 +`docs/input-rendering-optimization-20261103.md`。当前状态为:点击排序和客户端排队已改善, +仍需用户对真实拖窗、右键、快速双击进行体验验收;严格的输入到显示延迟仍需高速摄像或外部触发测量。 diff --git a/docs/rendering-execution-20261102.md b/docs/rendering-execution-20261102.md new file mode 100644 index 0000000..96ea5af --- /dev/null +++ b/docs/rendering-execution-20261102.md @@ -0,0 +1,168 @@ +# Windows 云桌面渲染优化执行记录(2026-11-02) + +## 执行顺序 + +1. 复核 GPU、实际编码器、客户机解码器、显示刷新率,保存原始证据。 +2. 用持续动画做同步参数 A/B;排除静态桌面的按需发帧影响。 +3. 在独立目录接通 MPP、RKMPP、DRM PRIME,先验证真实视频解码,再验证 Moonlight 串流。 +4. 只有真实串流通过后才启用新客户端,保留系统 Moonlight 的回退入口。 +5. GPU/NVENC 恢复后再做 1080p60、1440p30/60 和输入到屏幕延迟验收。 + +## 当前 GPU 证据 + +本轮经 Guest Agent 重新采集:GTX 965M 的 ConfigManagerErrorCode=43,NVML 查询失败, +DXGI 没有枚举到 NVIDIA 适配器,Sunshine 的最新实际编码器为 `libx264`。配置已是 +`sw_preset=ultrafast`,当前 Windows 虚拟屏为 2560x1440@30。 + +没有再次复位设备、重载 VFIO、替换 ROM 或重启宿主机。本轮未改变其他 VM。 +仍没有证据确认唯一故障原因;匹配此板卡的生产版 vBIOS 或另一块可稳定直通的 GPU +是下一轮服务端验证的候选条件,而非已完成修复。 + +验收工具更新为 `tools/validate-sunshine-nvenc.ps1` + `tools/probe-nvidia-d3d.cs`: +实际创建 NVIDIA D3D11 设备、检查 PnP 错误码、检查最新一次实际编码器创建及时间, +并核验 NVENC 会话与编码利用率。不会把启动阶段的编码器探测记录当作实际串流。 + +## 同步参数 A/B + +环境:系统 Moonlight 6.2.0,H.264 软件解码,2560x1440@20、30Mbps,输出约60Hz, +MATE compositor 已关闭,SDL OpenGL ES 2。Windows 用 +`tools/stream-motion-test.html` 持续移动条纹与棋盘;源 RAF 约30 FPS。 +125% DPI 下页面显示逻辑2048x1152,视频流和 HDMI 仍是物理2560x1440。 + +下表来自两段完整会话结束日志,包含启动/退出阶段,时长不同;属于现场初步对照, +不构成严格统计学基准。原始日志与叠图保存在 `evidence/moonlight-20261102/`。 + +| 指标 | smooth | low-latency | +|---|---:|---:| +| 接收/解码 FPS | 17.16 | 17.07 | +| 渲染 FPS | 17.07 | 17.01 | +| 主机处理平均 ms | 33.6 | 35.4 | +| 网络丢帧 | 0.00% | 0.00% | +| jitter 分类丢帧 | 0.44% | 0.29% | +| 网络延迟平均 ms | 11 | 11 | +| 解码平均 ms | 14.07 | 16.96 | +| 帧队列平均 ms | 0.95 | 1.14 | +| 呈现平均 ms(含同步) | 8.38 | 10.87 | + +系统客户端软件解码的对照中,关闭同步没有改善帧率或排队,因此软件基线保留 smooth。上述耗时 +不能直接相加当作端到端输入延迟;后续须用高速摄像/外部触发测量 P50/P95。 + +日志中的启动测试 EGLImage 导出失败和单次队列溢出,不能单独证明持续运行时故障。 +最终选中的系统路径是软件视频帧上传后的 SDL 呈现;`Using DRM renderer` 也不等于 +已经使用 VPU 或直接扫描输出。 + +## RKMPP 已验证部分 + +- 为 `/dev/mpp_service`、`/dev/rga`、`/dev/dma_heap/*` 增加 video 组读写权限; + 持久化规则为 `terminal/kiosk-shell/etc/70-vdi-rockchip-video.rules`。 +- 普通用户执行 MPP H.264 初始化,create/init/destroy 全部返回0。 +- 从固定提交构建 MPP 和 FFmpeg-Rockchip 6.1,目录是 + `/home/armbian/vdi-build/rkmpp-experiment`;依赖只下载解包,不安装或升级系统包。 +- 用 `generate-h264-sample.c` 生成60帧2560x1440 H.264样本。`h264_rkmpp` 全部解出,0错误。 +- CPU NV12输出耗时2.620秒;`-hwaccel rkmpp -hwaccel_output_format drm_prime` 输出耗时 + 0.290秒,CPU时间0.120秒,日志明确为 DRM PRIME 帧,0解码错误。 + +样本纹理简单且测试期间有编译负载;这些数字仅证明硬解/帧导出可工作,不能替代 +Moonlight 真实串流、显示呈现、复杂内容或2K60验收。 + +## 产品接入点 + +`VDI_STREAM_STATS=1` 独立于 profile,可公平比较两档。解码器覆盖只接受 +software/hardware/auto;RKMPP通过 `H264_DECODER_HINT=h264_rkmpp` 选择,不把 +`h264_rkmpp` 当作 Moonlight `--video-decoder` 的值。 + +`VDI_MOONLIGHT_PROGRAM` 可指定独立客户端启动器。`vdi-kiosk-start` 支持读取 +`~/.config/vdi-terminal/render.conf` 的 allowlist 字段,不执行配置文件: + +```ini +profile=smooth +stats=0 +program=/home/armbian/vdi-terminal/bin/vdi-moonlight-rkmpp +decoder=hardware +codec=H.264 +``` + +文件不存在时沿用系统客户端。上述为 smooth 档示例;本机实际部署档位见下表后说明。 + +## 构建与页面接入修正 + +私有客户端固定为 Moonlight v6.2.0 `de2467e433821664cdd2224aad8c89a625be1ad9`, +MPP `7916c3cc000fefc0bdf96636b7d54fe174724f44`,FFmpeg-Rockchip 6.1 +`797387de1c9caa9170100d7217ee093d1897f750`。复现入口为 +`tools/build-rkmpp-client.sh`,仅在盒子的用户目录下载、解包和构建依赖。 + +- Qt 5 构建需要显式为上游 `drm.h` 补入 ``,脚本幂等应用。 +- 补齐 EGL 开发依赖,重新执行 qmake 功能探测,并在缺少 HAVE_EGL 时终止构建。 +- qmake 改变功能开关不会自动使旧对象失效;构建脚本记录编译/链接参数指纹, + 参数变化时清理应用对象,防止“探测有 EGL、实际对象无 EGL”。 +- RKMPP 使用独立测试解码器,上游成功路径不输出旧的 `Test decode successful`, + 而输出 `Not reusing test decoder for h264_rkmpp`。页面就绪解析同时兼容两者, + 仍要求实际 renderer 已选中,并在连接失败时拒绝进入就绪状态。 +- 私有启动器设置 QML、Qt 插件和共享库目录,系统 Moonlight 不被覆盖。 + +第一轮真实串流已输出 2560x1440 的 `h264_rkmpp / drm_prime`,但显示选中 SDL, +并出现 `Selected hwframe->swframe ... (mapping: yes)`,因此该轮不作为零拷贝验收。 +对应证据为 `rkmpp-stale-sdl.txt` 与 `rkmpp-first.png`,包含构建负载和启动阶段。 + +另已核查用户注册表:`MouseTrails=0`、`DragFullWindows=1`,不通过添加第二个光标 +或重新设置透明光标来掩盖视频延迟。 + +## 后续顺序与验收要求 + +1. 先验证硬解实际串流与 EGL 呈现,记录持续动画下的接收、解码、呈现 FPS, + 解码/排队/呈现耗时及丢帧;没有通过则保留系统软件客户端默认入口。 +2. 通过后接入页面启动链,验证过场层退出、快捷键回列表、再次连接及持久配置。 + 保留2K20现有配置作为基线,只有实测更好才提高请求帧率。 +3. 服务端 GPU 修复需匹配板卡的生产 ROM 或可稳定直通的替代设备,并重复通过 + PnP、NVML、DXGI/D3D11、真实 NVENC 以及重启后的验收。 +4. 服务端恢复后按1080p60、1440p30、1440p60逐档验证,并同步虚拟屏刷新率。 + 1440p60还需要将当前30Hz虚拟屏调整到60Hz,不能只改客户端FPS参数。 +5. 用高速摄像或外部触发测输入到显示的P50/P95,另外观察快速拖窗与光标残影。 + 如延迟仍主要来自光标随视频传输,再评估服务端光标位置/形状侧通道与客户端 + 单光标显示的二次开发,需同时保证隐藏视频中的旧光标,不能直接叠加本地光标。 + +客户端应优先接通硬解与显示路径;镜像内安装补帧/运动插值插件不能修复 GPU Code 43, +也不能代替真实输入响应。当前不把插帧作为低延迟方案。 + +## 真实串流对照与现场选择 + +清理旧对象后,日志明确选择 `Renderer 'EGL/GLES' with 'DRM' backend chosen`, +实际解码尺寸2560x1440、`h264_rkmpp`、`drm_prime`,呈现使用 EGLImage。 +未见实际运行路径的 hwframe→swframe 映射回退。四组均为持续动画、30Mbps、 +源RAF30FPS、终端HDMI约60Hz、服务端libx264;均为约2分钟会话,统计包含启动/退出。 +会话按顺序进行,网络与服务端负载会波动,不能当作严格控制变量的实验室基准。 + +| 客户端/请求档位 | 接收/解码 FPS | 呈现 FPS | 主机平均 ms | 解码 ms | 队列 ms | 呈现 ms | 网络/jitter丢帧 | +|---|---:|---:|---:|---:|---:|---:|---| +| 系统软件解码 / 2K20 smooth(先前基线) | 17.16 | 17.07 | 33.6 | 14.07 | 0.95 | 8.38 | 0% / 0.44% | +| RKMPP+EGL / 2K20 smooth | 19.14 | 18.97 | 38.4 | 10.53 | 2.78 | 2.54 | 0% / 0.20% | +| RKMPP+EGL / 2K30 smooth | 24.21 | 23.29 | 46.2 | 8.24 | 31.95 | 4.19 | 0% / 3.02% | +| RKMPP+EGL / 2K30 low-latency | 23.56 | 23.46 | 46.8 | 9.11 | 2.39 | 5.41 | 0% / 0.10% | +| RKMPP+EGL / 2K30 VSync开、Frame Pacing关 | 23.26 | 22.63 | 47.5 | 8.28 | 28.93 | 4.14 | 0% / 2.23% | + +对应原始统计分别为 `rkmpp-egl-20-smooth.txt`、`rkmpp-egl-30-smooth.txt`、 +`rkmpp-egl-30-lowlatency.txt`、`rkmpp-egl-30-vsync-no-pacing.txt`,均在证据目录中。 + +本机选择2K30、`profile=low-latency`、RKMPP硬解、H.264,常态关闭叠图。 +这是在当前设备上的取舍:相较2K30 smooth,平均帧队列少约30ms;关闭VSync存在 +撕裂风险,需要现场动态观察。保留2K20 smooth作为平稳显示的回退档,不将其删除。 +截图可见单个白色远程光标,但静态截图不能证明快速移动没有残影或输入延迟为零。 + +当前会话平均呈现仍只有约23FPS,未达到请求的30FPS;服务端GPU故障和libx264路径 +仍未修复。不能将这一轮称为2K60、NVENC恢复或“鼠标无延迟”验收通过。 + +## 正式入口部署验证 + +已运行 `tools/deploy_kiosk.sh` 同步、编译、CTest、安装和重启盒子的kiosk受管栈; +CTest 2/2通过。render.conf选择上述私有硬解客户端,桌面卡请求帧率持久保存为30, +原文件备份在盒子 `~/.config/vdi-terminal/desktops.before-rkmpp-20261102.json`。 + +两次从实际列表页点击Windows连接,日志分别在约10.1秒、9.7秒进入stream_ready, +均明确选中 `EGL/GLES + DRM`、`h264_rkmpp`,过场层退出后画面可见。 +`Ctrl+Alt+Shift+Q` 正常退出code=0并回列表,再次连接通过;没有遗留第二条串流。 +配置已经写入现有自启链,但本轮未重启盒子、VM或宿主机;不声称完成重启后验收。 + +截图 `kiosk-rkmpp-session.png`、`kiosk-rkmpp-disconnect.png`、 +`kiosk-rkmpp-reconnect.png` 保留了产品入口验证。单白光标可见,未修改原光标策略。 +自动化拖窗未形成可计量的动态验收结果,仍需现场鼠标操作与外部摄像测量。 +专用Edge测试浏览器已关闭,临时VDIRenderBench任务已删除,未关闭其他浏览器。 diff --git a/terminal/kiosk-shell/CMakeLists.txt b/terminal/kiosk-shell/CMakeLists.txt index 56eabf9..4c974f8 100644 --- a/terminal/kiosk-shell/CMakeLists.txt +++ b/terminal/kiosk-shell/CMakeLists.txt @@ -55,6 +55,11 @@ target_link_libraries(vdi-kiosk-monitor PRIVATE Threads::Threads) if(BUILD_TESTING) find_package(Qt5 REQUIRED COMPONENTS Test) + if(UNIX) + find_package(Python3 REQUIRED COMPONENTS Interpreter) + add_test(NAME display-guard COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_display_guard.py) + endif() add_executable(test-moonlight-session tests/test_moonlight_session.cpp diff --git a/terminal/kiosk-shell/etc/70-vdi-rockchip-video.rules b/terminal/kiosk-shell/etc/70-vdi-rockchip-video.rules new file mode 100644 index 0000000..4fc390f --- /dev/null +++ b/terminal/kiosk-shell/etc/70-vdi-rockchip-video.rules @@ -0,0 +1,4 @@ +# RK3528 vendor-kernel media interfaces. Members of video may use the VPU. +KERNEL=="mpp_service", GROUP="video", MODE="0660" +KERNEL=="rga", GROUP="video", MODE="0660" +SUBSYSTEM=="dma_heap", GROUP="video", MODE="0660" diff --git a/terminal/kiosk-shell/etc/moonlight.conf b/terminal/kiosk-shell/etc/moonlight.conf index a10f5f6..fc6d8f1 100644 --- a/terminal/kiosk-shell/etc/moonlight.conf +++ b/terminal/kiosk-shell/etc/moonlight.conf @@ -18,6 +18,7 @@ # NVENC重启故障及各档性能记录见 docs/moonlight-kiosk-20261028.md。 # frame-pacing 1=开启帧率匀速(--frame-pacing),0=关闭(--no-frame-pacing) # performance-overlay 1=显示性能叠图(调试用) +# profile smooth / low-latency / diagnostic;为空时保留下方显式开关 # hang-timeout 串流假死判定时间(秒),0 表示关闭该检测 host=192.168.1.70 port=47984 @@ -34,6 +35,7 @@ absolute-mouse=1 vsync=1 frame-pacing=1 performance-overlay=0 +profile=smooth retry=3 retry-delay=5 hang-timeout=90 diff --git a/terminal/kiosk-shell/etc/vdi-display-guard.service b/terminal/kiosk-shell/etc/vdi-display-guard.service new file mode 100644 index 0000000..0cb4939 --- /dev/null +++ b/terminal/kiosk-shell/etc/vdi-display-guard.service @@ -0,0 +1,14 @@ +[Unit] +Description=VDI HDMI mode recovery after monitor power cycle (X11 POC) + +[Service] +Type=simple +Environment=DISPLAY=:0 +Environment=XAUTHORITY=%h/.Xauthority +ExecStart=%h/vdi-terminal/bin/vdi-display-guard +Restart=on-failure +RestartSec=5 +TimeoutStopSec=15 + +[Install] +WantedBy=default.target diff --git a/terminal/kiosk-shell/scripts/vdi-display-guard b/terminal/kiosk-shell/scripts/vdi-display-guard new file mode 100644 index 0000000..8602d9c --- /dev/null +++ b/terminal/kiosk-shell/scripts/vdi-display-guard @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +"""Restore this RK3528 terminal's verified HDMI timing after X11 hotplug.""" + +import argparse +import fcntl +import os +from pathlib import Path +import re +import signal +import subprocess +import sys +import time +from dataclasses import dataclass + +OUTPUT = "HDMI-1" +MODE = "2560x1440_60_fixed297" +# The vendor PHY rejects the EDID's 241.70 MHz timing. Keep the verified PLL. +TIMING = "297.00 2560 2608 2640 2720 1440 1443 1448 1820 +HSync -VSync".split() +POLL_SECONDS = 2 +SETTLE_SECONDS = 2 + + +def log(message): + print(time.strftime("%Y-%m-%d %H:%M:%S"), message, flush=True) + + +def xrandr(*args): + result = subprocess.run(["xrandr", *args], capture_output=True, text=True, + timeout=10, check=False) + if result.returncode: + raise RuntimeError((result.stderr or result.stdout).strip()[:400]) + return result.stdout + + +@dataclass +class DisplayState: + connected: bool + geometry: str + active_mode: str + modes: set + defined: bool + + @property + def healthy(self): + return (self.connected and self.geometry == "2560x1440+0+0" + and self.active_mode == MODE) + + +def parse_state(query): + connected, geometry, active = False, "", "" + modes, defined, in_output = set(), False, False + for line in query.splitlines(): + fields = line.split() + if not fields: + continue + if fields[0] == MODE: + defined = True # Includes a globally defined but detached mode. + if not line[0].isspace(): + in_output = fields[0] == OUTPUT + if in_output: + connected = len(fields) > 1 and fields[1] == "connected" + geometry = next((f for f in fields[2:] + if re.fullmatch(r"\d+x\d+\+\d+\+\d+", f)), "") + elif in_output and len(fields) > 1 and re.match(r"^\d+(?:\.\d+)?[+*]*$", fields[1]): + modes.add(fields[0]) + if "*" in " ".join(fields[1:]): + active = fields[0] + return DisplayState(connected, geometry, active, modes, defined) + + +class Guard: + def __init__(self, runner=xrandr, reporter=log): + self.run = runner + self.log = reporter + self.bad_since = None + self.next_retry = 0 + self.failures = 0 + self.last_status = None + + def report(self, status): + if status != self.last_status: + self.log(status) + self.last_status = status + + def step(self, now): + try: + # Xorg already receives DRM hotplug events. Read its current state + # without probing every EDID/mode: the vendor driver logs clock + # warnings for those probes, adding unnecessary work while streaming. + state = parse_state(self.run("--current")) + except (OSError, RuntimeError, subprocess.TimeoutExpired) as error: + self.bad_since = None + self.report("X11 unavailable; waiting: " + str(error)[:400]) + return False + if not state.connected: + self.bad_since = None + self.next_retry = 0 + self.failures = 0 + self.report(OUTPUT + " disconnected; no mode changes") + return False + if state.healthy: + self.bad_since = None + self.next_retry = 0 + self.failures = 0 + self.report("healthy: " + MODE) + return True + self.report("mode drift: " + (state.active_mode or "off") + " " + state.geometry) + if self.bad_since is None: + self.bad_since = now + if now - self.bad_since < SETTLE_SECONDS or now < self.next_retry: + return False + try: + if not state.defined: + self.run("--newmode", MODE, *TIMING) + if MODE not in state.modes: + self.run("--addmode", OUTPUT, MODE) + self.run("--output", OUTPUT, "--mode", MODE, "--pos", "0x0") + if not parse_state(self.run("--current")).healthy: + raise RuntimeError("mode was not retained after apply") + except (OSError, RuntimeError, subprocess.TimeoutExpired) as error: + self.failures = min(self.failures + 1, 6) + delay = min(60, 2 ** self.failures) + # Base backoff on completion, including a slow/timed-out modeset. + self.next_retry = time.monotonic() + delay + self.log("restore failed; retry in %ds: %s" % (delay, str(error)[:400])) + return False + self.bad_since = None + self.next_retry = 0 + self.failures = 0 + self.report("restored: " + MODE + " (stream process untouched)") + return True + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--once", action="store_true", + help="initial display recovery with a 20s retry window, then exit") + args = parser.parse_args() + os.environ.setdefault("DISPLAY", ":0") + os.environ.setdefault("XAUTHORITY", str(Path.home() / ".Xauthority")) + runtime = Path(os.environ.get("XDG_RUNTIME_DIR", str(Path.home() / ".cache/vdi-terminal"))) + runtime.mkdir(parents=True, exist_ok=True) + # Same lock for startup and the systemd service. Never run competing guards. + with (runtime / "vdi-display-guard.lock").open("a") as lock: + try: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + log("display guard already running") + return 0 + stop = False + + def request_stop(signum, frame): + nonlocal stop + stop = True + + signal.signal(signal.SIGTERM, request_stop) + signal.signal(signal.SIGINT, request_stop) + guard = Guard() + deadline = time.monotonic() + 20 + while not stop: + if guard.step(time.monotonic()) and args.once: + return 0 + if args.once and time.monotonic() >= deadline: + return 1 + time.sleep(POLL_SECONDS) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/terminal/kiosk-shell/scripts/vdi-kiosk-start b/terminal/kiosk-shell/scripts/vdi-kiosk-start index 4b46850..ec990b5 100755 --- a/terminal/kiosk-shell/scripts/vdi-kiosk-start +++ b/terminal/kiosk-shell/scripts/vdi-kiosk-start @@ -3,26 +3,28 @@ export DISPLAY="${DISPLAY:-:0}" export XAUTHORITY="${XAUTHORITY:-/home/armbian/.Xauthority}" -# Match the local output to the 2K Moonlight stream. -# This kernel rejects the monitor's 241.70 MHz timing but accepts the 297 MHz -# PLL rate. Longer vertical blanking produces 2560x1440 at approximately 60 Hz. -# HDMI can appear a few seconds after the desktop autostart entry runs. -: >/tmp/vdi-display-mode.log -mode="2560x1440_60_fixed297" -for _ in $(seq 1 20); do - if xrandr --query 2>>/tmp/vdi-display-mode.log | grep -q '^HDMI-1 connected'; then - xrandr --newmode "$mode" 297.00 \ - 2560 2608 2640 2720 1440 1443 1448 1820 \ - +HSync -VSync >>/tmp/vdi-display-mode.log 2>&1 || true - xrandr --addmode HDMI-1 "$mode" >>/tmp/vdi-display-mode.log 2>&1 || true - if xrandr --output HDMI-1 --mode "$mode" >>/tmp/vdi-display-mode.log 2>&1; then - break - fi - xrandr --output HDMI-1 --mode 1920x1080 --rate 60 \ - >>/tmp/vdi-display-mode.log 2>&1 || true - fi - sleep 1 -done +# Optional verified rendering deployment. Parse an allowlist as data; never +# source/eval configuration. Explicit environment overrides the saved defaults. +render_conf="${VDI_RENDER_CONF:-$HOME/.config/vdi-terminal/render.conf}" +if [ -r "$render_conf" ]; then + while IFS='=' read -r key value || [ -n "$key" ]; do + case "$key" in + profile) export VDI_STREAM_PROFILE="${VDI_STREAM_PROFILE:-$value}" ;; + stats) export VDI_STREAM_STATS="${VDI_STREAM_STATS:-$value}" ;; + program) export VDI_MOONLIGHT_PROGRAM="${VDI_MOONLIGHT_PROGRAM:-$value}" ;; + decoder) export VDI_MOONLIGHT_DECODER="${VDI_MOONLIGHT_DECODER:-$value}" ;; + codec) export VDI_MOONLIGHT_CODEC="${VDI_MOONLIGHT_CODEC:-$value}" ;; + esac + done < "$render_conf" +fi + +# Use the same idempotent recovery for boot and HDMI hotplug. The user service +# supervises ongoing recovery; a singleton lock prevents competing instances. +"$(dirname "$0")/vdi-display-guard" --once >>/tmp/vdi-display-mode.log 2>&1 || true +if ! systemctl --user start vdi-display-guard.service >>/tmp/vdi-display-mode.log 2>&1; then + echo 'WARNING: display guard service unavailable; only boot recovery active' \ + >>/tmp/vdi-display-mode.log +fi exec /home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- \ /home/armbian/vdi-terminal/bin/kiosk-shell diff --git a/terminal/kiosk-shell/scripts/vdi-moonlight b/terminal/kiosk-shell/scripts/vdi-moonlight index 360f3fe..9867868 100644 --- a/terminal/kiosk-shell/scripts/vdi-moonlight +++ b/terminal/kiosk-shell/scripts/vdi-moonlight @@ -3,7 +3,7 @@ # # 职责: # 1. 读取本地配置(主机、应用、分辨率、帧率、码率、编解码) -# 2. 以软件解码拉起 moonlight-qt,全屏串流云桌面 +# 2. 按配置拉起 Moonlight,全屏串流云桌面 # 3. 写心跳文件,供 vdi-kiosk-monitor 判断是否假死 # 4. 检测串流假死(CPU 与网络均无流量)并杀掉重连 # @@ -12,7 +12,8 @@ # # 说明:RK3528 当前没有可用的 V4L2 request/m2m 硬解设备,moonlight-qt 的 # --video-decoder auto 会选到 h264_v4l2m2m 并报 "Could not find a valid device", -# 因此配置里显式指定 decoder=software。接入 MPP/VPU 硬解后再改回 auto。 +# 因此系统客户端默认 decoder=software;验证后的独立 RKMPP 客户端 +# 通过 VDI_MOONLIGHT_PROGRAM 与 decoder=hardware 显式启用。 set -u @@ -38,9 +39,10 @@ DECODER="software" DISPLAY_MODE="fullscreen" AUDIO=1 ABSOLUTE_MOUSE=1 -VSYNC=0 -FRAME_PACING=0 +VSYNC=1 +FRAME_PACING=1 PERFORMANCE_OVERLAY=0 +PROFILE="" RETRY=3 RETRY_DELAY=5 HANG_TIMEOUT=90 @@ -76,6 +78,7 @@ load_config() { vsync) VSYNC="$value" ;; frame-pacing) FRAME_PACING="$value" ;; performance-overlay) PERFORMANCE_OVERLAY="$value" ;; + profile) PROFILE="$value" ;; retry) RETRY="$value" ;; retry-delay) RETRY_DELAY="$value" ;; hang-timeout) HANG_TIMEOUT="$value" ;; @@ -84,6 +87,33 @@ load_config() { done < "$CONF" } +apply_profile() { + case "$PROFILE" in + smooth) + VSYNC=1 + FRAME_PACING=1 + ;; + low-latency) + VSYNC=0 + FRAME_PACING=0 + ;; + diagnostic) + VSYNC=1 + FRAME_PACING=1 + PERFORMANCE_OVERLAY=1 + ;; + "") + # 兼容旧配置:没有profile时保留moonlight.conf中的显式开关。 + ;; + *) + log "未知串流 profile=$PROFILE,回退 smooth" + PROFILE=smooth + VSYNC=1 + FRAME_PACING=1 + ;; + esac +} + # 默认路由出接口的累计收字节数,用于判断串流是否还有数据流入 rx_bytes() { iface=$(awk '$2=="00000000" {print $1; exit}' /proc/net/route) @@ -145,7 +175,7 @@ disable_screensaver() { build_cmd() { # moonlight-qt stream 只接受 位置参数,没有 --port 选项, # 服务端端口由 moonlight 自身按 GameStream 协议协商。 - CMD="moonlight-qt stream $HOST $APP" + CMD="${VDI_MOONLIGHT_PROGRAM:-moonlight-qt} stream $HOST $APP" CMD="$CMD --resolution ${WIDTH}x${HEIGHT}" CMD="$CMD --fps $FPS" CMD="$CMD --bitrate $BITRATE" @@ -155,7 +185,7 @@ build_cmd() { CMD="$CMD --keep-awake" [ "$ABSOLUTE_MOUSE" = "1" ] && CMD="$CMD --absolute-mouse" [ "$AUDIO" = "1" ] && CMD="$CMD --audio-on-host" || CMD="$CMD --no-audio-on-host" - # 低延迟开关:默认关闭 vsync 与 frame-pacing,削减帧队列/呈现延迟 + # 按profile选择同步策略;默认保持当前稳定档。 [ "$VSYNC" = "1" ] && CMD="$CMD --vsync" || CMD="$CMD --no-vsync" [ "$FRAME_PACING" = "1" ] && CMD="$CMD --frame-pacing" || CMD="$CMD --no-frame-pacing" [ "$PERFORMANCE_OVERLAY" = "1" ] && CMD="$CMD --performance-overlay" @@ -163,7 +193,7 @@ build_cmd() { run_stream() { build_cmd - log "启动串流: $CMD" + log "启动串流 profile=${PROFILE:-config} decoder=$DECODER codec=$CODEC vsync=$VSYNC frame-pacing=$FRAME_PACING renderer=${SDL_RENDER_DRIVER:-} video-driver=${SDL_VIDEODRIVER:-}: $CMD" # shellcheck disable=SC2086 $CMD >> "$LOG_FILE" 2>&1 & ML_PID=$! @@ -220,6 +250,19 @@ stop) esac load_config +PROFILE="${VDI_STREAM_PROFILE:-$PROFILE}" +apply_profile +DECODER="${VDI_MOONLIGHT_DECODER:-$DECODER}" +CODEC="${VDI_MOONLIGHT_CODEC:-$CODEC}" +case "$DECODER" in +software|hardware|auto) ;; +*) log "未知解码器 $DECODER,回退 software"; DECODER=software ;; +esac +case "$CODEC" in +H.264|HEVC|AV1|auto) ;; +*) log "未知视频编码 $CODEC,回退 H.264"; CODEC=H.264 ;; +esac +PERFORMANCE_OVERLAY="${VDI_STREAM_STATS:-$PERFORMANCE_OVERLAY}" if [ -z "$HOST" ]; then log "配置缺失或缺少 host: $CONF" exit 2 @@ -227,6 +270,10 @@ fi export DISPLAY="${VDI_DISPLAY:-:0}" export XAUTHORITY="${VDI_XAUTHORITY:-$HOME/.Xauthority}" +export SDL_RENDER_DRIVER="${VDI_SDL_RENDER_DRIVER:-opengles2}" +export SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR="${VDI_SDL_BYPASS_COMPOSITOR:-1}" +[ -n "${VDI_SDL_FORCE_EGL:-}" ] && export SDL_VIDEO_X11_FORCE_EGL="$VDI_SDL_FORCE_EGL" +[ -n "${VDI_SDL_VIDEODRIVER:-}" ] && export SDL_VIDEODRIVER="$VDI_SDL_VIDEODRIVER" disable_screensaver kill_stale diff --git a/terminal/kiosk-shell/scripts/vdi-moonlight-rkmpp b/terminal/kiosk-shell/scripts/vdi-moonlight-rkmpp new file mode 100644 index 0000000..4ffb191 --- /dev/null +++ b/terminal/kiosk-shell/scripts/vdi-moonlight-rkmpp @@ -0,0 +1,20 @@ +#!/bin/sh +# Select explicitly after RKMPP decode + real stream validation. Does not replace +# the distribution Moonlight or add a second cursor. +set -eu +task_root="${VDI_RKMPP_BUILD_ROOT:-$HOME/vdi-build/rkmpp-experiment}" +client="${VDI_RKMPP_CLIENT:-$task_root/build/moonlight/app/moonlight}" +if [ ! -x "$client" ] || [ ! -r /dev/mpp_service ] || [ ! -w /dev/mpp_service ]; then + echo 'RKMPP client or device access unavailable' >&2 + exit 78 +fi +export LD_LIBRARY_PATH="$task_root/runtime/lib:$task_root/deps/usr/lib/aarch64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +export QML2_IMPORT_PATH="$task_root/deps/usr/lib/aarch64-linux-gnu/qt5/qml${QML2_IMPORT_PATH:+:$QML2_IMPORT_PATH}" +export QT_PLUGIN_PATH="$task_root/deps/usr/lib/aarch64-linux-gnu/qt5/plugins${QT_PLUGIN_PATH:+:$QT_PLUGIN_PATH}" +export GL_IS_SLOW="${GL_IS_SLOW:-0}" +export H264_DECODER_HINT=h264_rkmpp +export HEVC_DECODER_HINT=hevc_rkmpp +# Only affects unpaced rendering in the patched client. Leave the last presented +# frame and decoder references intact; replace only waiting decoded frames. +export VDI_LATEST_FRAME="${VDI_LATEST_FRAME:-1}" +exec "$client" "$@" diff --git a/terminal/kiosk-shell/src/desktop_list_page.cpp b/terminal/kiosk-shell/src/desktop_list_page.cpp index a74f071..f279329 100644 --- a/terminal/kiosk-shell/src/desktop_list_page.cpp +++ b/terminal/kiosk-shell/src/desktop_list_page.cpp @@ -82,10 +82,10 @@ void DesktopListPage::rebuildCards() { for (const DesktopInfo& d : desktops_) { auto* card = new QFrame(this); + card->setObjectName(QStringLiteral("desktopCard")); card->setMinimumHeight(96); card->setStyleSheet(QStringLiteral( - "QFrame { background: #1a2233; border: 1px solid #2b3654; border-radius: 10px; }" - "QFrame:hover { border: 1px solid #2f6feb; }" + "QFrame#desktopCard { background: #1a2233; border: 1px solid #2b3654; border-radius: 10px; }" "QLabel { border: none; background: transparent; }")); auto* row = new QHBoxLayout(card); diff --git a/terminal/kiosk-shell/src/main_window.cpp b/terminal/kiosk-shell/src/main_window.cpp index b93a55c..a549bd3 100644 --- a/terminal/kiosk-shell/src/main_window.cpp +++ b/terminal/kiosk-shell/src/main_window.cpp @@ -64,7 +64,6 @@ void MainWindow::setupUi() { QStringLiteral("QWidget { background: #0f1420; color: #e6e9ef; }" "QLineEdit { background: #1a2233; border: 1px solid #2b3654; " "border-radius: 4px; padding: 8px; color: #e6e9ef; }" - "QLineEdit:focus { border: 1px solid #2f6feb; }" "QPushButton { background: #232d44; border: 1px solid #2b3654; " "border-radius: 6px; color: #e6e9ef; }" "QPushButton:hover { background: #2b3654; }")); @@ -289,11 +288,8 @@ void MainWindow::onEditDesktopRequested(const QString& desktopId) { } bool MainWindow::startMoonlightSession(const ConnectionTicket& ticket, quint64 attempt) { - MoonlightSession::Options opts; - opts.decoder = QStringLiteral("software"); - opts.videoCodec = QStringLiteral("H.264"); - opts.audioOnHost = true; - moonlightSession_->setOptions(opts); + moonlightSession_->setOptions(MoonlightSession::optionsFromEnvironment( + QProcessEnvironment::systemEnvironment())); phaseClock_.restart(); connectionPhase_ = ConnectionPhase::kProcessStart; diff --git a/terminal/kiosk-shell/src/moonlight_session.cpp b/terminal/kiosk-shell/src/moonlight_session.cpp index 33a09f5..791bc41 100644 --- a/terminal/kiosk-shell/src/moonlight_session.cpp +++ b/terminal/kiosk-shell/src/moonlight_session.cpp @@ -14,7 +14,11 @@ constexpr int kReadyStableMs = 750; constexpr int kTerminateGraceMs = 2000; bool isTestDecodeLine(const QByteArray& line) { - return line.toLower().contains("test decode successful"); + const QByteArray lower = line.toLower(); + // Hardware decoders use a separate test context. Upstream logs this only + // after that context has successfully decoded and validated a test frame. + return lower.contains("test decode successful") || + lower.contains("not reusing test decoder for "); } bool isRendererLine(const QByteArray& line) { @@ -105,6 +109,66 @@ void MoonlightSession::setOptions(const Options& options) { options_ = options; } +MoonlightSession::Options MoonlightSession::optionsFromEnvironment( + const QProcessEnvironment& environment) { + Options options; + const QString profile = environment.value(QStringLiteral("VDI_STREAM_PROFILE")); + if (profile == QStringLiteral("low-latency")) { + options.profile = profile; + options.vsync = false; + options.framePacing = false; + } else if (profile == QStringLiteral("diagnostic")) { + options.profile = profile; + options.performanceOverlay = true; + } + // Statistics must be independently selectable for a fair profile A/B test. + if (environment.contains(QStringLiteral("VDI_STREAM_STATS"))) { + options.performanceOverlay = environment.value(QStringLiteral("VDI_STREAM_STATS")) == QStringLiteral("1"); + } + const QString decoder = environment.value(QStringLiteral("VDI_MOONLIGHT_DECODER")); + if (QStringList{QStringLiteral("software"), QStringLiteral("hardware"), + QStringLiteral("auto")}.contains(decoder)) { + options.decoder = decoder; + } + const QString codec = environment.value(QStringLiteral("VDI_MOONLIGHT_CODEC")); + if (QStringList{QStringLiteral("H.264"), QStringLiteral("HEVC"), + QStringLiteral("AV1"), QStringLiteral("auto")}.contains(codec)) { + options.videoCodec = codec; + } + const QString program = environment.value(QStringLiteral("VDI_MOONLIGHT_PROGRAM")); + if (!program.isEmpty()) { + options.program = program; + } + return options; +} + +QStringList MoonlightSession::buildStreamArguments(const ConnectionTicket& ticket, + const Options& options) { + const int bitrateKbps = ticket.width >= 2560 ? 30000 : 20000; + QStringList args; + args << QStringLiteral("stream") + << ticket.endpoint + << ticket.app + << QStringLiteral("--resolution") + << QStringLiteral("%1x%2").arg(ticket.width).arg(ticket.height) + << QStringLiteral("--fps") << QString::number(ticket.fps) + << QStringLiteral("--bitrate") << QString::number(bitrateKbps) + << QStringLiteral("--display-mode") << QStringLiteral("fullscreen") + << QStringLiteral("--video-codec") << options.videoCodec + << QStringLiteral("--video-decoder") << options.decoder + << QStringLiteral("--absolute-mouse") + << QStringLiteral("--keep-awake") + << (options.vsync ? QStringLiteral("--vsync") : QStringLiteral("--no-vsync")) + << (options.framePacing ? QStringLiteral("--frame-pacing") + : QStringLiteral("--no-frame-pacing")); + if (options.performanceOverlay) { + args << QStringLiteral("--performance-overlay"); + } + args << (options.audioOnHost ? QStringLiteral("--audio-on-host") + : QStringLiteral("--no-audio-on-host")); + return args; +} + bool MoonlightSession::isRunning() const { return process_->state() != QProcess::NotRunning; } @@ -156,34 +220,26 @@ bool MoonlightSession::start(const ConnectionTicket& ticket, EventCallback callb readyTimer_->stop(); killTimer_->stop(); - process_->setProgram(QStringLiteral("moonlight-qt")); + process_->setProgram(options_.program); QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); environment.insert(QStringLiteral("GL_IS_SLOW"), QStringLiteral("0")); - environment.insert(QStringLiteral("SDL_RENDER_DRIVER"), QStringLiteral("opengles2")); + environment.insert(QStringLiteral("SDL_RENDER_DRIVER"), + environment.value(QStringLiteral("VDI_SDL_RENDER_DRIVER"), QStringLiteral("opengles2"))); + // Xorg下绕过桌面合成器;Wayland/KMS部署时可通过SDL_VIDEODRIVER切换, + // 不在客户端里硬编码会话类型,避免当前Xorg镜像无法启动。 + environment.insert(QStringLiteral("SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"), + environment.value(QStringLiteral("VDI_SDL_BYPASS_COMPOSITOR"), QStringLiteral("1"))); + const QString forceEgl = qEnvironmentVariable("VDI_SDL_FORCE_EGL"); + if (!forceEgl.isEmpty()) { + environment.insert(QStringLiteral("SDL_VIDEO_X11_FORCE_EGL"), forceEgl); + } + const QString sdlVideoDriver = qEnvironmentVariable("VDI_SDL_VIDEODRIVER"); + if (!sdlVideoDriver.isEmpty()) { + environment.insert(QStringLiteral("SDL_VIDEODRIVER"), sdlVideoDriver); + } process_->setProcessEnvironment(environment); - const int bitrateKbps = ticket.width >= 2560 ? 30000 : 20000; - QStringList args; - args << QStringLiteral("stream") - << ticket.endpoint - << ticket.app - << QStringLiteral("--resolution") - << QStringLiteral("%1x%2").arg(ticket.width).arg(ticket.height) - << QStringLiteral("--fps") << QString::number(ticket.fps) - << QStringLiteral("--bitrate") << QString::number(bitrateKbps) - << QStringLiteral("--display-mode") << QStringLiteral("fullscreen") - << QStringLiteral("--video-codec") << options_.videoCodec - << QStringLiteral("--video-decoder") << options_.decoder - << QStringLiteral("--absolute-mouse") - << QStringLiteral("--keep-awake"); - args << (options_.vsync ? QStringLiteral("--vsync") : QStringLiteral("--no-vsync")); - args << (options_.framePacing ? QStringLiteral("--frame-pacing") - : QStringLiteral("--no-frame-pacing")); - if (options_.performanceOverlay) { - args << QStringLiteral("--performance-overlay"); - } - args << (options_.audioOnHost ? QStringLiteral("--audio-on-host") - : QStringLiteral("--no-audio-on-host")); + const QStringList args = buildStreamArguments(ticket, options_); process_->setArguments(args); const QString logDir = QDir::homePath() + QStringLiteral("/vdi-terminal/logs"); @@ -191,6 +247,23 @@ bool MoonlightSession::start(const ConnectionTicket& ticket, EventCallback callb logPath_ = logDir + QStringLiteral("/moonlight-%1.log") .arg(QDate::currentDate().toString(QStringLiteral("yyyyMMdd"))); + // 把最终生效的环境和参数写入日志,避免只看Moonlight叠图时无法判断 + // 产品入口到底使用了哪一套呈现策略。 + QFile launchLog(logPath_); + if (launchLog.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { + QTextStream stream(&launchLog); + stream << "\n[VDI launch]\n" + << "profile=" << options_.profile << "\n" + << "program=" << options_.program << "\n" + << "decoder=" << options_.decoder << "\n" + << "codec=" << options_.videoCodec << "\n" + << "vsync=" << (options_.vsync ? "1" : "0") << "\n" + << "frame-pacing=" << (options_.framePacing ? "1" : "0") << "\n" + << "renderer=" << environment.value(QStringLiteral("SDL_RENDER_DRIVER")) << "\n" + << "video-driver=" << environment.value(QStringLiteral("SDL_VIDEODRIVER")) << "\n" + << "args=" << args.join(QLatin1Char(' ')) << "\n"; + } + process_->start(); return true; } diff --git a/terminal/kiosk-shell/src/moonlight_session.h b/terminal/kiosk-shell/src/moonlight_session.h index d9f29d1..ddc2a17 100644 --- a/terminal/kiosk-shell/src/moonlight_session.h +++ b/terminal/kiosk-shell/src/moonlight_session.h @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include @@ -37,7 +39,13 @@ class MoonlightSession : public QObject { Q_OBJECT public: + // 产品入口与调试脚本遵循相同的profile语义。 + // smooth:优先稳定呈现,适合当前2K20/2K30; + // low-latency:关闭客户端额外的同步等待,适合1080p60 A/B; + // diagnostic:在smooth基础上打开Moonlight性能叠图。 struct Options { + QString program = QStringLiteral("moonlight-qt"); + QString profile = QStringLiteral("smooth"); bool vsync = true; bool framePacing = true; bool performanceOverlay = false; @@ -64,6 +72,9 @@ public: ~MoonlightSession() override; void setOptions(const Options& options); + static Options optionsFromEnvironment(const QProcessEnvironment& environment); + static QStringList buildStreamArguments(const ConnectionTicket& ticket, + const Options& options); bool start(const ConnectionTicket& ticket, EventCallback callback); void stop(); bool isRunning() const; diff --git a/terminal/kiosk-shell/tests/test_display_guard.py b/terminal/kiosk-shell/tests/test_display_guard.py new file mode 100644 index 0000000..1c0cb0e --- /dev/null +++ b/terminal/kiosk-shell/tests/test_display_guard.py @@ -0,0 +1,140 @@ +"""Failure and hotplug scenarios; no real display or sleep required.""" + +from pathlib import Path +import runpy +import unittest +from unittest.mock import patch + +guard_module = runpy.run_path(str(Path(__file__).resolve().parents[1] / "scripts/vdi-display-guard")) +Guard = guard_module["Guard"] +MODE = guard_module["MODE"] + + +class FakeRandR: + def __init__(self): + self.connected = True + self.defined = True + self.attached = True + self.active = "1920x1440" + self.writes = [] + self.fail_apply = False + self.query_error = False + self.keep_wrong_mode = False + + def __call__(self, *args): + if args == ("--current",): + if self.query_error: + raise RuntimeError("Can't open display :0") + status = "connected primary " + ("2560x1440+0+0" if self.active == MODE else "1920x1440+0+0") + if not self.connected: + status = "disconnected" + query = "Screen 0: current 1920 x 1440\nHDMI-1 " + status + " (normal)\n" + if self.connected: + query += " 1920x1440 75.00" + ("*" if self.active != MODE else "") + "\n" + if self.attached: + query += " " + MODE + " 60.00" + ("*" if self.active == MODE else "") + "\n" + if self.defined and (not self.attached or not self.connected): + query += " " + MODE + " (0x189) 297.000MHz +HSync -VSync\n" + query += " h: width 2560 start 2608\n v: height 1440 start 1443\n" + return query + self.writes.append(args) + if args[0] == "--newmode": + if self.defined: + raise RuntimeError("BadName: duplicate mode") + self.defined = True + elif args[0] == "--addmode": + assert self.defined + self.attached = True + elif args[0] == "--output": + if self.fail_apply: + raise RuntimeError("Configure crtc failed") + assert self.attached + if not self.keep_wrong_mode: + self.active = MODE + return "" + + +class DisplayGuardTests(unittest.TestCase): + def setUp(self): + self.xr = FakeRandR() + self.logs = [] + self.guard = Guard(self.xr, self.logs.append) + + def tick(self, now): + with patch("time.monotonic", return_value=now): + return self.guard.step(now) + + def test_mode_drift_restores_after_settling(self): + self.assertFalse(self.tick(0)) + self.assertEqual(self.xr.writes, []) + self.assertTrue(self.tick(2)) + self.assertEqual(len(self.xr.writes), 1) + self.assertEqual(self.xr.active, MODE) + + def test_healthy_display_does_not_modeset_or_repeat_logs(self): + self.xr.active = MODE + for now in range(0, 100, 2): + self.assertTrue(self.tick(now)) + self.assertEqual(self.xr.writes, []) + self.assertEqual(len(self.logs), 1) + + def test_disconnected_never_writes_and_reconnect_settles(self): + self.tick(0) + self.xr.connected = False + for now in range(2, 20, 2): + self.assertFalse(self.tick(now)) + self.assertEqual(self.xr.writes, []) + self.xr.connected = True + self.assertFalse(self.tick(20)) + self.assertTrue(self.tick(22)) + + def test_new_x_server_recreates_and_attaches_mode(self): + self.xr.defined = self.xr.attached = False + self.tick(0) + self.assertTrue(self.tick(2)) + self.assertEqual([w[0] for w in self.xr.writes], ["--newmode", "--addmode", "--output"]) + + def test_hotplug_detached_mode_is_not_created_twice(self): + self.xr.attached = False + self.tick(0) + self.assertTrue(self.tick(2)) + self.assertEqual([w[0] for w in self.xr.writes], ["--addmode", "--output"]) + + def test_late_desktop_settings_overwrite_is_corrected_again(self): + self.tick(0) + self.assertTrue(self.tick(2)) + self.xr.active = "1920x1440" + self.assertFalse(self.tick(4)) + self.assertTrue(self.tick(6)) + self.assertEqual(len(self.xr.writes), 2) + + def test_modeset_failure_backs_off_and_eventually_recovers(self): + self.xr.fail_apply = True + self.tick(0) + self.tick(2) # retry at 4 + self.tick(4) # retry at 8 + self.tick(6) # no write + self.assertEqual(len(self.xr.writes), 2) + self.xr.fail_apply = False + self.assertTrue(self.tick(8)) + self.assertEqual(len(self.xr.writes), 3) + + def test_apply_success_must_be_verified(self): + self.xr.keep_wrong_mode = True + self.tick(0) + self.assertFalse(self.tick(2)) + self.assertIn("not retained", self.logs[-1]) + + def test_x11_unavailable_does_not_write_and_then_recovers(self): + self.xr.query_error = True + for now in range(0, 20, 2): + self.assertFalse(self.tick(now)) + self.assertEqual(self.xr.writes, []) + self.assertEqual(len(self.logs), 1) + self.xr.query_error = False + self.assertFalse(self.tick(20)) + self.assertTrue(self.tick(22)) + + +if __name__ == "__main__": + unittest.main() diff --git a/terminal/kiosk-shell/tests/test_moonlight_session.cpp b/terminal/kiosk-shell/tests/test_moonlight_session.cpp index 498d936..d387f13 100644 --- a/terminal/kiosk-shell/tests/test_moonlight_session.cpp +++ b/terminal/kiosk-shell/tests/test_moonlight_session.cpp @@ -10,7 +10,11 @@ class MoonlightSessionTest : public QObject { private slots: void parsesSplitReadyMarkers(); void requiresBothReadyMarkers(); + void acceptsSeparateHardwareTestDecoder(); void rejectsSecondStart(); + void buildsConsistentStreamArguments(); + void selectsProfileWithoutLosingStats(); + void rejectsInvalidDecoderOverride(); }; void MoonlightSessionTest::parsesSplitReadyMarkers() { @@ -38,6 +42,19 @@ void MoonlightSessionTest::requiresBothReadyMarkers() { QVERIFY(!parser.isReady()); } +void MoonlightSessionTest::acceptsSeparateHardwareTestDecoder() { + MoonlightOutputParser parser; + parser.append("Using custom H.264 decoder (H264_DECODER_HINT): h264_rkmpp\n"); + QVERIFY(!parser.isReady()); + parser.append("Not reusing test decoder for h264_rkmpp\n"); + QVERIFY(parser.hasTestDecode()); + QVERIFY(!parser.isReady()); + parser.append("Renderer 'EGL' with 'DRM' backend chosen\n"); + QVERIFY(parser.isReady()); + parser.append("Connection terminated: -1\n"); + QVERIFY(!parser.isReady()); +} + void MoonlightSessionTest::rejectsSecondStart() { QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); @@ -90,5 +107,61 @@ void MoonlightSessionTest::rejectsSecondStart() { qputenv("PATH", oldPath); } +void MoonlightSessionTest::buildsConsistentStreamArguments() { + ConnectionTicket ticket; + ticket.endpoint = QStringLiteral("192.168.1.70"); + ticket.app = QStringLiteral("Desktop"); + ticket.width = 2560; + ticket.height = 1440; + ticket.fps = 30; + + MoonlightSession::Options options; + options.profile = QStringLiteral("low-latency"); + options.decoder = QStringLiteral("software"); + options.videoCodec = QStringLiteral("H.264"); + options.vsync = false; + options.framePacing = false; + options.performanceOverlay = true; + + const QStringList args = MoonlightSession::buildStreamArguments(ticket, options); + QVERIFY(args.contains(QStringLiteral("--resolution"))); + QVERIFY(args.contains(QStringLiteral("2560x1440"))); + QVERIFY(args.contains(QStringLiteral("--fps"))); + QVERIFY(args.contains(QStringLiteral("30"))); + QVERIFY(args.contains(QStringLiteral("--bitrate"))); + QVERIFY(args.contains(QStringLiteral("30000"))); + QVERIFY(args.contains(QStringLiteral("--no-vsync"))); + QVERIFY(args.contains(QStringLiteral("--no-frame-pacing"))); + QVERIFY(args.contains(QStringLiteral("--performance-overlay"))); + QVERIFY(args.contains(QStringLiteral("--absolute-mouse"))); +} + +void MoonlightSessionTest::selectsProfileWithoutLosingStats() { + QProcessEnvironment environment; + environment.insert(QStringLiteral("VDI_STREAM_PROFILE"), QStringLiteral("low-latency")); + environment.insert(QStringLiteral("VDI_STREAM_STATS"), QStringLiteral("1")); + auto options = MoonlightSession::optionsFromEnvironment(environment); + QVERIFY(!options.vsync); + QVERIFY(!options.framePacing); + QVERIFY(options.performanceOverlay); + environment.insert(QStringLiteral("VDI_STREAM_PROFILE"), QStringLiteral("smooth")); + options = MoonlightSession::optionsFromEnvironment(environment); + QVERIFY(options.vsync); + QVERIFY(options.framePacing); + QVERIFY(options.performanceOverlay); +} + +void MoonlightSessionTest::rejectsInvalidDecoderOverride() { + QProcessEnvironment environment; + environment.insert(QStringLiteral("VDI_MOONLIGHT_DECODER"), QStringLiteral("h264_rkmpp")); + environment.insert(QStringLiteral("VDI_STREAM_PROFILE"), QStringLiteral("invalid")); + auto options = MoonlightSession::optionsFromEnvironment(environment); + QCOMPARE(options.decoder, QStringLiteral("software")); + QCOMPARE(options.profile, QStringLiteral("smooth")); + environment.insert(QStringLiteral("VDI_MOONLIGHT_DECODER"), QStringLiteral("hardware")); + options = MoonlightSession::optionsFromEnvironment(environment); + QCOMPARE(options.decoder, QStringLiteral("hardware")); +} + QTEST_GUILESS_MAIN(MoonlightSessionTest) #include "test_moonlight_session.moc" diff --git a/terminal/moonlight/README.md b/terminal/moonlight/README.md new file mode 100644 index 0000000..64c2538 --- /dev/null +++ b/terminal/moonlight/README.md @@ -0,0 +1,40 @@ +# VDI Moonlight 交互优化补丁 + +本目录维护当前 RK3528 / Xorg POC 私有客户端的源码修改,不替换系统 Moonlight。 + +基线:Moonlight Qt v6.2.0 `de2467e433821664cdd2224aad8c89a625be1ad9`,其检出的 moonlight-common-c 子模块保持对应版本。MPP、FFmpeg 版本仍由 `tools/build-rkmpp-client.sh` 固定。 + +## 补丁 + +- `0001-vdi-input-and-latest-frame.patch` 在 Moonlight Qt 根目录应用。鼠标移动只合并队首连续事件,遇到按钮、键盘、窗口、呈现事件或其他鼠标设备立即停止;每次最多额外合并 64 个事件。绝对模式在发送按键前使用该按键事件的坐标。低延迟队列可选地把已等待的解码帧替换成新帧。 +- `0002-vdi-absolute-click-barrier.patch` 在 `moonlight-common-c/moonlight-common-c` 中应用。鼠标按钮封住此前的位置批次,后续移动只能进入新批次;发送线程只锁定自己的批次,避免旧包出队时误清除新批次。所有包依旧走原可靠鼠标通道,保留 1ms 合并限流和原坐标边界修正。 +- `0003-vdi-input-priority.patch` 在 Qt 根目录应用。可选地让帧就绪后的连续输入先执行,最多 32 条或 2ms,不能跨窗口/焦点/其他事件。`VDI_INPUT_PRIORITY=1` 才启用,默认关闭。另提供 `VDI_INPUT_TIMING=1` 的汇总统计,区分 SDL 输入派发年龄与主线程呈现调用耗时,不代表端到端延迟。 + +2026-11-03 的现场 RKMPP/EGL 使用独立 PacerRender 线程,诊断会话主线程 render 调用数为 0;因此第三补丁的新调度没有已证明的现场收益,未启用。诊断候选二进制保存为 `moonlight.input-priority-20261103`;默认客户端已恢复到本轮前 SHA256 `35de7b1a4ade583823b23b5f2e012307f6d0a3848c8eb97bd6d4de9660afa00f`。详细计划/初测限制见 `docs/mouse-optimization-plan-20261103.md`,独立光标接入设计见 `docs/independent-cursor-design-20261103.md`。 + +`VDI_LATEST_FRAME=1` 只在没有 VSync pacing source 的呈现队列生效。最多保留一个待呈现解码帧,丢弃数计入原 `pacerDroppedFrames`。不丢压缩包,不改变 H.264 参考链,也不提前释放正在呈现或延迟释放的上一帧。未设置或设为 `0` 时保留上游帧队列策略;kiosk 私有 wrapper 默认设为 `1`。smooth 档仍使用原 pacing 行为。 + +这些补丁不增加本地光标,不隐藏 Windows 光标,不改变控制协议。远程光标仍随视频显示,其延迟仍受服务器捕获/编码、网络、解码和显示影响。 + +## 构建与测试 + +在盒子上运行构建器时,将本目录的 `patches/` 一起复制过去,通过 `VDI_MOONLIGHT_PATCH_DIR` 指定目录。构建器先检查固定提交,再检查补丁:已完整应用则跳过;上下文不匹配则失败,不强行覆盖其他修改。 + +```bash +VDI_BUILD_STAGE=moonlight \ +VDI_MOONLIGHT_PATCH_DIR="$HOME/vdi-build/rkmpp-experiment/patches" \ +bash tools/build-rkmpp-client.sh +``` + +`tests/` 的文件复制到盒子 `$VDI_RKMPP_BUILD_ROOT/latency-tests/` 后运行: + +```bash +bash tools/test-moonlight-latency.sh +VDI_TEST_SANITIZE=1 ASAN_OPTIONS=detect_leaks=1 bash tools/test-moonlight-latency.sh +``` + +测试直接包含实际修改后的 SDL helper、AVFrame 队列 helper 与 `InputStream.c`,并链接客户端实际使用的 native 队列/线程库。覆盖跨点击合并、键盘/呈现边界、工作量上限、触摸隔离、200 帧的积压替换、已呈现帧所有权及 1,000 轮“移动→按下→移动→释放→移动”的发送线程滞后场景。测试显式启用断言,避免上游 release 头文件的 NDEBUG 使检查失效。 + +新增 `test_input_priority.cpp` 验证优先调度不越过焦点/窗口/退出/解码器重置事件,保持输入 FIFO,隔离其他窗口与合成触摸,并验证数量与时间限制。普通与 ASAN 版本均通过。当前 RKMPP/EGL 没有主线程呈现事件,新调度开关不能用作该路径的性能提升宣传。 + +现场数据、局限与完整回退入口见 `docs/input-rendering-optimization-20261103.md`。 diff --git a/terminal/moonlight/patches/0001-vdi-input-and-latest-frame.patch b/terminal/moonlight/patches/0001-vdi-input-and-latest-frame.patch new file mode 100644 index 0000000..405c5cd --- /dev/null +++ b/terminal/moonlight/patches/0001-vdi-input-and-latest-frame.patch @@ -0,0 +1,271 @@ +--- a/app/streaming/input/mouse.cpp ++++ b/app/streaming/input/mouse.cpp +@@ -1,4 +1,5 @@ + #include "input.h" ++#include "vdi_mouse_batch.h" + + #include + #include "SDL_compat.h" +@@ -84,6 +85,11 @@ + button = BUTTON_RIGHT; + } + ++ // Button coordinates are captured with this event, not a later mouse state. ++ if (m_AbsoluteMouseMode) { ++ sendAbsoluteMousePosition(event->x, event->y); ++ } ++ + LiSendMouseButtonEvent(event->state == SDL_PRESSED ? + BUTTON_ACTION_PRESS : + BUTTON_ACTION_RELEASE, +@@ -101,81 +107,78 @@ + return; + } + +- // Batch all pending mouse motion events to save CPU time +- Sint32 x = event->x, y = event->y, xrel = event->xrel, yrel = event->yrel; +- SDL_Event nextEvent; +- while (SDL_PeepEvents(&nextEvent, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) { +- event = &nextEvent.motion; +- +- // Ignore synthetic mouse events +- if (event->which != SDL_TOUCH_MOUSEID) { +- x = event->x; +- y = event->y; +- xrel += event->xrel; +- yrel += event->yrel; +- } +- } ++ // Coalesce only adjacent motions. Never consume motion past a click/key ++ // or render event, and bound work so a fast mouse cannot starve rendering. ++ SDL_MouseMotionEvent batched = *event; ++ vdiBatchMouseMotion(batched); ++ Sint32 x = batched.x, y = batched.y; ++ Sint32 xrel = batched.xrel, yrel = batched.yrel; + + // We should not reference the original event anymore + event = nullptr; + + if (m_AbsoluteMouseMode) { +- int windowWidth, windowHeight; +- SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight); +- +- SDL_Rect src, dst; +- bool mouseInVideoRegion; +- +- src.x = src.y = 0; +- src.w = m_StreamWidth; +- src.h = m_StreamHeight; +- +- dst.x = dst.y = 0; +- dst.w = windowWidth; +- dst.h = windowHeight; +- +- // Use the stream and window sizes to determine the video region +- StreamUtils::scaleSourceToDestinationSurface(&src, &dst); +- +- mouseInVideoRegion = isMouseInVideoRegion(x, y, windowWidth, windowHeight); +- +- // Clamp motion to the video region +- x = qMin(qMax(x - dst.x, 0), dst.w); +- y = qMin(qMax(y - dst.y, 0), dst.h); +- +- // Send the mouse position update if one of the following is true: +- // a) it is in the video region now +- // b) it just left the video region (to ensure the mouse is clamped to the video boundary) +- // c) a mouse button is still down from before the cursor left the video region (to allow smooth dragging) +- Uint32 buttonState = SDL_GetMouseState(nullptr, nullptr); +- if (buttonState == 0) { +- if (m_PendingMouseButtonsAllUpOnVideoRegionLeave) { +- if (m_NeedsManualCaptureOnLeave) { +- // Stop capturing the mouse now +- SDL_CaptureMouse(SDL_FALSE); +- } +- m_PendingMouseButtonsAllUpOnVideoRegionLeave = false; +- } +- } +- if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) { +- LiSendMousePositionEvent((short)x, (short)y, dst.w, dst.h); +- } +- +- // Adjust the cursor visibility if applicable +- if (mouseInVideoRegion ^ m_MouseWasInVideoRegion) { +- SDL_ShowCursor((mouseInVideoRegion && m_MouseCursorCapturedVisibilityState == SDL_DISABLE) ? SDL_DISABLE : SDL_ENABLE); +- if (!mouseInVideoRegion && buttonState != 0) { +- // If we still have a button pressed on leave, wait for that to come up +- // before we stop sending mouse position events. +- m_PendingMouseButtonsAllUpOnVideoRegionLeave = true; +- } +- } +- +- m_MouseWasInVideoRegion = mouseInVideoRegion; ++ sendAbsoluteMousePosition(x, y); + } + else { + LiSendMouseMoveEvent(xrel, yrel); + } ++} ++ ++void SdlInputHandler::sendAbsoluteMousePosition(Sint32 x, Sint32 y) ++{ ++ int windowWidth, windowHeight; ++ SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight); ++ ++ SDL_Rect src, dst; ++ bool mouseInVideoRegion; ++ ++ src.x = src.y = 0; ++ src.w = m_StreamWidth; ++ src.h = m_StreamHeight; ++ ++ dst.x = dst.y = 0; ++ dst.w = windowWidth; ++ dst.h = windowHeight; ++ ++ // Use the stream and window sizes to determine the video region ++ StreamUtils::scaleSourceToDestinationSurface(&src, &dst); ++ ++ mouseInVideoRegion = isMouseInVideoRegion(x, y, windowWidth, windowHeight); ++ ++ // Clamp motion to the video region ++ x = qMin(qMax(x - dst.x, 0), dst.w); ++ y = qMin(qMax(y - dst.y, 0), dst.h); ++ ++ // Send the mouse position update if one of the following is true: ++ // a) it is in the video region now ++ // b) it just left the video region (to ensure the mouse is clamped to the video boundary) ++ // c) a mouse button is still down from before the cursor left the video region (to allow smooth dragging) ++ Uint32 buttonState = SDL_GetMouseState(nullptr, nullptr); ++ if (buttonState == 0) { ++ if (m_PendingMouseButtonsAllUpOnVideoRegionLeave) { ++ if (m_NeedsManualCaptureOnLeave) { ++ // Stop capturing the mouse now ++ SDL_CaptureMouse(SDL_FALSE); ++ } ++ m_PendingMouseButtonsAllUpOnVideoRegionLeave = false; ++ } ++ } ++ if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) { ++ LiSendMousePositionEvent((short)x, (short)y, dst.w, dst.h); ++ } ++ ++ // Adjust the cursor visibility if applicable ++ if (mouseInVideoRegion ^ m_MouseWasInVideoRegion) { ++ SDL_ShowCursor((mouseInVideoRegion && m_MouseCursorCapturedVisibilityState == SDL_DISABLE) ? SDL_DISABLE : SDL_ENABLE); ++ if (!mouseInVideoRegion && buttonState != 0) { ++ // If we still have a button pressed on leave, wait for that to come up ++ // before we stop sending mouse position events. ++ m_PendingMouseButtonsAllUpOnVideoRegionLeave = true; ++ } ++ } ++ ++ m_MouseWasInVideoRegion = mouseInVideoRegion; + } + + void SdlInputHandler::handleMouseWheelEvent(SDL_MouseWheelEvent* event) +--- a/app/streaming/input/input.h ++++ b/app/streaming/input/input.h +@@ -95,6 +95,8 @@ + void handleMouseButtonEvent(SDL_MouseButtonEvent* event); + + void handleMouseMotionEvent(SDL_MouseMotionEvent* event); ++ ++ void sendAbsoluteMousePosition(Sint32 x, Sint32 y); + + void handleMouseWheelEvent(SDL_MouseWheelEvent* event); + +--- /dev/null ++++ b/app/streaming/input/vdi_mouse_batch.h +@@ -0,0 +1,22 @@ ++#pragma once ++#include ++ ++// Only the SDL main thread consumes events; other threads may append events. ++inline void vdiBatchMouseMotion(SDL_MouseMotionEvent& motion) ++{ ++ for (int i = 0; i < 64; ++i) { ++ SDL_Event next; ++ if (SDL_PeepEvents(&next, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) != 1 || ++ next.type != SDL_MOUSEMOTION || next.motion.which != motion.which || ++ next.motion.windowID != motion.windowID || next.motion.state != motion.state) { ++ break; ++ } ++ if (SDL_PeepEvents(&next, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) != 1) { ++ break; ++ } ++ motion.x = next.motion.x; ++ motion.y = next.motion.y; ++ motion.xrel += next.motion.xrel; ++ motion.yrel += next.motion.yrel; ++ } ++} +--- a/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp ++++ b/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp +@@ -1,4 +1,5 @@ + #include "pacer.h" ++#include "vdi_latest_frame.h" + #include "streaming/streamutils.h" + + #ifdef Q_OS_WIN32 +@@ -34,6 +35,7 @@ + m_VsyncThread(nullptr), + m_DeferredFreeFrame(nullptr), + m_Stopping(false), ++ m_LatestFrameOnly(qEnvironmentVariableIntValue("VDI_LATEST_FRAME") == 1), + m_VsyncSource(nullptr), + m_VsyncRenderer(renderer), + m_MaxVideoFps(0), +@@ -178,7 +180,14 @@ + + void Pacer::enqueueFrameForRenderingAndUnlock(AVFrame *frame) + { +- dropFrameForEnqueue(m_RenderQueue); ++ if (m_LatestFrameOnly && m_VsyncSource == nullptr) { ++ // Drop only decoded, never presented frames. Keep the decoder reference ++ // chain and the deferred free of the last presented frame intact. ++ m_VideoStats->pacerDroppedFrames += vdiDiscardQueuedFrames(m_RenderQueue); ++ } ++ else { ++ dropFrameForEnqueue(m_RenderQueue); ++ } + m_RenderQueue.enqueue(frame); + + m_FrameQueueLock.unlock(); +@@ -261,6 +270,8 @@ + + bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enablePacing) + { ++ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "VDI latest-frame queue: %s", ++ (m_LatestFrameOnly && !enablePacing) ? "enabled" : "disabled"); + m_MaxVideoFps = maxVideoFps; + m_DisplayFps = StreamUtils::getDisplayRefreshRate(window); + m_RendererAttributes = m_VsyncRenderer->getRendererAttributes(); +--- a/app/streaming/video/ffmpeg-renderers/pacer/pacer.h ++++ b/app/streaming/video/ffmpeg-renderers/pacer/pacer.h +@@ -68,6 +68,7 @@ + SDL_Thread* m_VsyncThread; + AVFrame* m_DeferredFreeFrame; + bool m_Stopping; ++ bool m_LatestFrameOnly; + + IVsyncSource* m_VsyncSource; + IFFmpegRenderer* m_VsyncRenderer; +--- /dev/null ++++ b/app/streaming/video/ffmpeg-renderers/pacer/vdi_latest_frame.h +@@ -0,0 +1,16 @@ ++#pragma once ++#include ++extern "C" { ++#include ++} ++ ++inline unsigned vdiDiscardQueuedFrames(QQueue& queue) ++{ ++ unsigned dropped = 0; ++ while (!queue.isEmpty()) { ++ AVFrame* stale = queue.dequeue(); ++ av_frame_free(&stale); ++ ++dropped; ++ } ++ return dropped; ++} diff --git a/terminal/moonlight/patches/0002-vdi-absolute-click-barrier.patch b/terminal/moonlight/patches/0002-vdi-absolute-click-barrier.patch new file mode 100644 index 0000000..a5d3e40 --- /dev/null +++ b/terminal/moonlight/patches/0002-vdi-absolute-click-barrier.patch @@ -0,0 +1,143 @@ +--- a/src/InputStream.c ++++ b/src/InputStream.c +@@ -86,11 +86,16 @@ + int deltaX, deltaY; + bool dirty; // Update ready to send (queued packet holder in packetQueue) + } currentRelativeMouseState; +-static struct { +- int x, y; +- int width, height; +- bool dirty; // Update ready to send (queued packet holder in packetQueue) +-} currentAbsoluteMouseState; ++// Only this packet may absorb newer positions. A button event seals the batch. ++static PPACKET_HOLDER currentQueuedAbsoluteMousePacket; ++ ++static void latchAbsoluteMousePacket(PPACKET_HOLDER holder) { ++ PltLockMutex(&batchedInputMutex); ++ if (currentQueuedAbsoluteMousePacket == holder) { ++ currentQueuedAbsoluteMousePacket = NULL; ++ } ++ PltUnlockMutex(&batchedInputMutex); ++} + + // Initializes the input stream + int initializeInputStream(void) { +@@ -120,7 +125,7 @@ + + memset(currentGamepadSensorState, 0, sizeof(currentGamepadSensorState)); + memset(¤tRelativeMouseState, 0, sizeof(currentRelativeMouseState)); +- memset(¤tAbsoluteMouseState, 0, sizeof(currentAbsoluteMouseState)); ++ currentQueuedAbsoluteMousePacket = NULL; + PltCreateMutex(&batchedInputMutex); + + return 0; +@@ -444,24 +449,9 @@ + now = PltGetMillis(); + } + +- PltLockMutex(&batchedInputMutex); +- +- // Populate the packet with the latest state +- holder->packet.mouseMoveAbs.x = BE16(currentAbsoluteMouseState.x); +- holder->packet.mouseMoveAbs.y = BE16(currentAbsoluteMouseState.y); +- +- // There appears to be a rounding error in GFE's scaling calculation which prevents +- // the cursor from reaching the far edge of the screen when streaming at smaller +- // resolutions with a higher desktop resolution (like streaming 720p with a desktop +- // resolution of 1080p, or streaming 720p/1080p with a desktop resolution of 4K). +- // Subtracting one from the reference dimensions seems to work around this issue. +- holder->packet.mouseMoveAbs.width = BE16(currentAbsoluteMouseState.width - 1); +- holder->packet.mouseMoveAbs.height = BE16(currentAbsoluteMouseState.height - 1); +- +- // The state change is no longer pending +- currentAbsoluteMouseState.dirty = false; +- +- PltUnlockMutex(&batchedInputMutex); ++ // Payload was filled by the producer under the same mutex. Latch ++ // only our packet; a later batch may already be queued after a click. ++ latchAbsoluteMousePacket(holder); + + lastMousePacketTime = now; + } +@@ -780,52 +770,37 @@ + } + + PltLockMutex(&batchedInputMutex); +- +- // Overwrite the previous mouse location with the new one +- currentAbsoluteMouseState.x = x; +- currentAbsoluteMouseState.y = y; +- currentAbsoluteMouseState.width = referenceWidth; +- currentAbsoluteMouseState.height = referenceHeight; +- +- // Queue a packet holder if this is the only pending absolute mouse event +- if (!currentAbsoluteMouseState.dirty) { +- // Set the dirty flag to claim ownership of inserting the packet holder +- // and unlock to allow other threads to enqueue or process input. +- currentAbsoluteMouseState.dirty = true; +- PltUnlockMutex(&batchedInputMutex); +- ++ holder = currentQueuedAbsoluteMousePacket; ++ bool newPacket = holder == NULL; ++ if (newPacket) { + holder = allocatePacketHolder(0); + if (holder == NULL) { +- currentAbsoluteMouseState.dirty = false; ++ PltUnlockMutex(&batchedInputMutex); + return -1; + } +- + holder->channelId = CTRL_CHANNEL_MOUSE; +- +- // TODO: Send this as unreliable sequenced when we have a delayed reliable retransmission thread + holder->enetPacketFlags = ENET_PACKET_FLAG_RELIABLE; +- + holder->packet.mouseMoveAbs.header.size = BE32(sizeof(NV_ABS_MOUSE_MOVE_PACKET) - sizeof(uint32_t)); + holder->packet.mouseMoveAbs.header.magic = LE32(MOUSE_MOVE_ABS_MAGIC); + holder->packet.mouseMoveAbs.unused = 0; +- +- // Remaining fields are set in the input thread based on the latest currentAbsoluteMouseState values +- ++ } ++ holder->packet.mouseMoveAbs.x = BE16(x); ++ holder->packet.mouseMoveAbs.y = BE16(y); ++ // Preserve upstream's far-edge rounding workaround. ++ holder->packet.mouseMoveAbs.width = BE16(referenceWidth - 1); ++ holder->packet.mouseMoveAbs.height = BE16(referenceHeight - 1); ++ err = 0; ++ if (newPacket) { ++ currentQueuedAbsoluteMousePacket = holder; + err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry); + if (err != LBQ_SUCCESS) { ++ currentQueuedAbsoluteMousePacket = NULL; + LC_ASSERT(err == LBQ_BOUND_EXCEEDED); + Limelog("Input queue reached maximum size limit\n"); + freePacketHolder(holder); +- +- // We weren't able to insert the entry, so let the next call try again +- currentAbsoluteMouseState.dirty = false; +- } +- } +- else { +- // There's already a packet holder queued to send this event +- PltUnlockMutex(&batchedInputMutex); +- err = 0; +- } ++ } ++ } ++ PltUnlockMutex(&batchedInputMutex); + + // This is not thread safe, but it's not a big deal because callers that want to + // use LiSendRelativeMotionAsMousePositionEvent() must not mix these function +@@ -872,7 +847,11 @@ + holder->packet.mouseButton.header.magic = LE32(holder->packet.mouseButton.header.magic); + holder->packet.mouseButton.button = (uint8_t)button; + ++ PltLockMutex(&batchedInputMutex); ++ // Do not let a later position overwrite the move preceding this click. ++ currentQueuedAbsoluteMousePacket = NULL; + err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry); ++ PltUnlockMutex(&batchedInputMutex); + if (err != LBQ_SUCCESS) { + LC_ASSERT(err == LBQ_BOUND_EXCEEDED); + Limelog("Input queue reached maximum size limit\n"); diff --git a/terminal/moonlight/patches/0003-vdi-input-priority.patch b/terminal/moonlight/patches/0003-vdi-input-priority.patch new file mode 100644 index 0000000..23bdfbd --- /dev/null +++ b/terminal/moonlight/patches/0003-vdi-input-priority.patch @@ -0,0 +1,179 @@ +--- a/app/streaming/session.cpp ++++ b/app/streaming/session.cpp +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include "input/vdi_input_priority.h" + #include + + #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +@@ -1962,8 +1963,14 @@ + + // Hijack this thread to be the SDL main thread. We have to do this + // because we want to suspend all Qt processing until the stream is over. ++ VdiInputPriority vdiInputPriority(qEnvironmentVariableIntValue("VDI_INPUT_PRIORITY") == 1, ++ SDL_CODE_FRAME_READY); ++ VdiInputTiming vdiInputTiming(qEnvironmentVariableIntValue("VDI_INPUT_TIMING") == 1); ++ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "VDI input priority: %s", ++ qEnvironmentVariableIntValue("VDI_INPUT_PRIORITY") == 1 ? "enabled" : "disabled"); + SDL_Event event; + for (;;) { ++ if (!vdiInputPriority.takeDeferred(event, SDL_GetWindowID(m_Window))) { + #if SDL_VERSION_ATLEAST(2, 0, 18) && !defined(STEAM_LINK) + // SDL 2.0.18 has a proper wait event implementation that uses platform + // support to block on events rather than polling on Windows, macOS, X11, +@@ -1995,6 +2002,11 @@ + continue; + } + #endif ++ if (vdiInputPriority.deferFrame(event)) { ++ continue; ++ } ++ } ++ vdiInputTiming.observe(event, SDL_GetWindowID(m_Window)); + switch (event.type) { + case SDL_QUIT: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, +@@ -2005,7 +2017,9 @@ + switch (event.user.code) { + case SDL_CODE_FRAME_READY: + if (m_VideoDecoder != nullptr) { ++ const auto renderStart = vdiInputTiming.startRender(); + m_VideoDecoder->renderFrameOnMainThread(); ++ vdiInputTiming.endRender(renderStart); + } + break; + case SDL_CODE_FLUSH_WINDOW_EVENT_BARRIER: +@@ -2308,6 +2322,7 @@ + } + + DispatchDeferredCleanup: ++ vdiInputTiming.report(vdiInputPriority.prioritized()); + // Switch back to synchronous logging mode + StreamUtils::exitAsyncLoggingMode(); + +--- /dev/null ++++ b/app/streaming/input/vdi_input_priority.h +@@ -0,0 +1,121 @@ ++#pragma once ++#include ++#include ++#include ++#include ++ ++// SDL main thread only. Inputs may pass one frame-ready notification, never a ++// window/focus/quit/other notification or another frame. Input FIFO is preserved. ++class VdiInputPriority { ++public: ++ VdiInputPriority(bool enabled, Sint32 frameCode) ++ : m_Enabled(enabled), m_FrameCode(frameCode) {} ++ ++ bool deferFrame(const SDL_Event& event) { ++ if (!m_Enabled || event.type != SDL_USEREVENT || event.user.code != m_FrameCode) ++ return false; ++ SDL_assert(!m_Pending); ++ m_Frame = event; ++ m_Pending = true; ++ m_Count = 0; ++ m_Start = SDL_GetPerformanceCounter(); ++ return true; ++ } ++ ++ bool takeDeferred(SDL_Event& event, Uint32 windowId) { ++ if (!m_Pending) return false; ++ SDL_Event next; ++ const auto elapsed = SDL_GetPerformanceCounter() - m_Start; ++ if (m_Count < 32 && elapsed < SDL_GetPerformanceFrequency() / 500 && ++ SDL_PeepEvents(&next, 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1 && ++ isInputForWindow(next, windowId) && ++ SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1) { ++ ++m_Count; ++ ++m_Prioritized; ++ return true; ++ } ++ event = m_Frame; ++ m_Pending = false; ++ return true; ++ } ++ ++ uint64_t prioritized() const { return m_Prioritized; } ++ ++ static bool isInputForWindow(const SDL_Event& event, Uint32 windowId) { ++ switch (event.type) { ++ case SDL_MOUSEMOTION: ++ return event.motion.windowID == windowId && event.motion.which != SDL_TOUCH_MOUSEID; ++ case SDL_MOUSEBUTTONDOWN: ++ case SDL_MOUSEBUTTONUP: ++ return event.button.windowID == windowId && event.button.which != SDL_TOUCH_MOUSEID; ++ case SDL_MOUSEWHEEL: ++ return event.wheel.windowID == windowId && event.wheel.which != SDL_TOUCH_MOUSEID; ++ case SDL_KEYDOWN: ++ case SDL_KEYUP: ++ return event.key.windowID == windowId; ++ default: ++ return false; ++ } ++ } ++ ++private: ++ bool m_Enabled; ++ Sint32 m_FrameCode; ++ bool m_Pending = false; ++ SDL_Event m_Frame{}; ++ unsigned m_Count = 0; ++ Uint64 m_Start = 0; ++ uint64_t m_Prioritized = 0; ++}; ++ ++// Aggregated only when explicitly requested. Ages are SDL queue/dispatch ages, ++// not network RTT or physical input-to-photon latency. 0.1 ms histogram bins. ++class VdiInputTiming { ++public: ++ explicit VdiInputTiming(bool enabled) : m_Enabled(enabled) {} ++ void observe(const SDL_Event& event, Uint32 windowId) { ++ if (!m_Enabled || !event.common.timestamp || ++ !VdiInputPriority::isInputForWindow(event, windowId)) return; ++ const Uint32 age = SDL_GetTicks() - event.common.timestamp; ++ // Ignore invalid/future synthetic timestamps; subtraction handles wrap. ++ if (age < 60000) m_Input.add(Uint64(age) * 10); ++ } ++ Uint64 startRender() const { return m_Enabled ? SDL_GetPerformanceCounter() : 0; } ++ void endRender(Uint64 start) { ++ if (m_Enabled) m_Render.add((SDL_GetPerformanceCounter() - start) * 10000 / SDL_GetPerformanceFrequency()); ++ } ++ void report(uint64_t prioritized) const { ++ if (!m_Enabled) return; ++ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, ++ "VDI input timing: SDL dispatch age only; prioritized=%llu", ++ (unsigned long long)prioritized); ++ m_Input.report("input dispatch age"); ++ m_Render.report("main-thread render call"); ++ } ++private: ++ struct Histogram { ++ std::array bins{}; ++ uint64_t count = 0, total = 0, max = 0; ++ void add(uint64_t tenths) { ++ ++bins[std::min(tenths, bins.size()-1)]; ++ ++count; total += tenths; max = std::max(max, tenths); ++ } ++ double quantile(unsigned percent) const { ++ const uint64_t rank = (count * percent + 99) / 100; ++ uint64_t accumulated = 0; ++ for (size_t i = 0; i < bins.size(); ++i) { ++ accumulated += bins[i]; ++ if (accumulated >= rank) return i / 10.0; ++ } ++ return 0; ++ } ++ void report(const char* label) const { ++ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, ++ "VDI %s: samples=%llu avg=%.2f p50=%.1f p95=%.1f max=%.1f ms (percentiles capped at 102.3ms)", ++ label, (unsigned long long)count, count ? total / (10.0*count) : 0, ++ count ? quantile(50) : 0, count ? quantile(95) : 0, max / 10.0); ++ } ++ }; ++ bool m_Enabled; ++ Histogram m_Input, m_Render; ++}; diff --git a/terminal/moonlight/tests/test_absolute_input.c b/terminal/moonlight/tests/test_absolute_input.c new file mode 100644 index 0000000..a7fab8f --- /dev/null +++ b/terminal/moonlight/tests/test_absolute_input.c @@ -0,0 +1,63 @@ +// Include the production implementation so batching can be inspected without +// starting a real network connection. Unused transport code is linker-collected. +#include "InputStream.c" +#undef NDEBUG +#include + +static PPACKET_HOLDER pop(void) { + PPACKET_HOLDER holder = NULL; + assert(LbqPollQueueElement(&packetQueue, (void**)&holder) == LBQ_SUCCESS); + return holder; +} + +static void expectPosition(int x) { + PPACKET_HOLDER holder = pop(); + assert(holder->packet.header.magic == LE32(MOUSE_MOVE_ABS_MAGIC)); + latchAbsoluteMousePacket(holder); + assert(holder->packet.mouseMoveAbs.x == BE16(x)); + assert((uint16_t)holder->packet.mouseMoveAbs.width == (uint16_t)BE16(2559)); + freePacketHolder(holder); +} + +int main(void) { + AppVersionQuad[0] = 7; + LbqInitializeLinkedBlockingQueue(&packetQueue, MAX_QUEUED_INPUT_PACKETS); + LbqInitializeLinkedBlockingQueue(&packetHolderFreeList, MAX_QUEUED_INPUT_PACKETS); + PltCreateMutex(&batchedInputMutex); + initialized = true; + for (int i = 0; i < 1000; ++i) { + // A deliberately stalled sender must preserve each click's coordinates. + assert(LiSendMousePositionEvent(10, 10, 2560, 1440) == 0); + assert(LiSendMousePositionEvent(20, 20, 2560, 1440) == 0); + assert(LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT) == 0); + assert(LiSendMousePositionEvent(30, 30, 2560, 1440) == 0); + assert(LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT) == 0); + assert(LiSendMousePositionEvent(40, 40, 2560, 1440) == 0); + expectPosition(20); + PPACKET_HOLDER press = pop(); + assert(press->packet.header.magic == LE32(BUTTON_ACTION_PRESS + 1)); + freePacketHolder(press); + expectPosition(30); + PPACKET_HOLDER release = pop(); + assert(release->packet.header.magic == LE32(BUTTON_ACTION_RELEASE + 1)); + freePacketHolder(release); + // Latching an older packet above must not prevent the latest batch + // from still absorbing newer positions. + assert(LiSendMousePositionEvent(50, 50, 2560, 1440) == 0); + expectPosition(50); + PPACKET_HOLDER empty; + assert(LbqPollQueueElement(&packetQueue, (void**)&empty) == LBQ_NO_ELEMENT); + } + initialized = false; + PLINKED_BLOCKING_QUEUE_ENTRY entry = LbqDestroyLinkedBlockingQueue(&packetQueue); + assert(entry == NULL); + entry = LbqDestroyLinkedBlockingQueue(&packetHolderFreeList); + while (entry) { + PLINKED_BLOCKING_QUEUE_ENTRY next = entry->flink; + free(entry->data); + entry = next; + } + PltDeleteMutex(&batchedInputMutex); + puts("PASS: 1000 queued move/press/move/release cycles preserve positions"); + return 0; +} diff --git a/terminal/moonlight/tests/test_input_priority.cpp b/terminal/moonlight/tests/test_input_priority.cpp new file mode 100644 index 0000000..2b5ad0a --- /dev/null +++ b/terminal/moonlight/tests/test_input_priority.cpp @@ -0,0 +1,86 @@ +#include "vdi_input_priority.h" +#undef NDEBUG +#include +#include + +constexpr Sint32 frameCode = 123; +static SDL_Event frame() { + SDL_Event e{}; e.type = SDL_USEREVENT; e.user.code = frameCode; return e; +} +static SDL_Event input(Uint32 type, Uint32 window = 1) { + SDL_Event e{}; e.type = type; e.key.windowID = window; return e; +} +static void push(SDL_Event e) { assert(SDL_PushEvent(&e) == 1); } +static SDL_Event pop() { + SDL_Event e{}; + assert(SDL_PeepEvents(&e,1,SDL_GETEVENT,SDL_FIRSTEVENT,SDL_LASTEVENT)==1); + return e; +} +int main() { + assert(SDL_Init(SDL_INIT_EVENTS)==0); + SDL_Event out{}; + VdiInputPriority disabled(false, frameCode); + push(input(SDL_MOUSEMOTION)); + assert(!disabled.deferFrame(frame()) && !disabled.takeDeferred(out,1)); + assert(pop().type == SDL_MOUSEMOTION); + + // Only a contiguous input prefix can pass a frame. No focus/quit/decoder + // barrier may be skipped, even if input is waiting further down the queue. + for (Uint32 barrier : {Uint32(SDL_WINDOWEVENT),Uint32(SDL_QUIT), + Uint32(SDL_USEREVENT),Uint32(SDL_RENDER_DEVICE_RESET), + Uint32(SDL_CONTROLLERDEVICEADDED)}) { + VdiInputPriority scheduler(true, frameCode); + push(input(SDL_MOUSEBUTTONDOWN)); push(input(SDL_MOUSEBUTTONUP)); + push(input(barrier)); push(input(SDL_KEYDOWN)); + assert(scheduler.deferFrame(frame())); + assert(scheduler.takeDeferred(out,1) && out.type==SDL_MOUSEBUTTONDOWN); + assert(scheduler.takeDeferred(out,1) && out.type==SDL_MOUSEBUTTONUP); + assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT && out.user.code==frameCode); + assert(!scheduler.takeDeferred(out,1)); + assert(pop().type==barrier && pop().type==SDL_KEYDOWN); + assert(scheduler.prioritized()==2); + } + // Preserve all input order, including keyboard releases and drag edges. + { + VdiInputPriority scheduler(true,frameCode); + for (Uint32 t : {Uint32(SDL_KEYDOWN),Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONDOWN), + Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONUP),Uint32(SDL_KEYUP)}) push(input(t)); + assert(scheduler.deferFrame(frame())); + for (Uint32 t : {Uint32(SDL_KEYDOWN),Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONDOWN), + Uint32(SDL_MOUSEMOTION),Uint32(SDL_MOUSEBUTTONUP),Uint32(SDL_KEYUP)}) + assert(scheduler.takeDeferred(out,1) && out.type==t); + assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT); + } + for (bool synthetic : {false,true}) { + VdiInputPriority scheduler(true,frameCode); + auto e=input(SDL_MOUSEMOTION,synthetic?1:2); + if(synthetic) e.motion.which=SDL_TOUCH_MOUSEID; + push(e); assert(scheduler.deferFrame(frame())); + assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT); + assert(pop().type==SDL_MOUSEMOTION); + } + // A high-rate input device cannot starve video. + { + VdiInputPriority scheduler(true,frameCode); + for(int i=0;i<1000;++i) push(input(SDL_MOUSEWHEEL)); + assert(scheduler.deferFrame(frame())); + unsigned n=0; + while(scheduler.takeDeferred(out,1) && out.type!=SDL_USEREVENT) ++n; + assert(n<=32 && n>0 && out.type==SDL_USEREVENT); + SDL_FlushEvents(SDL_FIRSTEVENT,SDL_LASTEVENT); + } + { + VdiInputPriority scheduler(true,frameCode); + push(input(SDL_MOUSEBUTTONUP)); + assert(scheduler.deferFrame(frame())); SDL_Delay(4); + assert(scheduler.takeDeferred(out,1) && out.type==SDL_USEREVENT); + assert(pop().type==SDL_MOUSEBUTTONUP); + } + // Instrumentation never consumes events or changes cursor visibility. + VdiInputTiming timing(true); + auto e=input(SDL_MOUSEMOTION); e.common.timestamp=SDL_GetTicks(); + timing.observe(e,1); auto start=timing.startRender(); timing.endRender(start); + timing.report(0); + SDL_Quit(); + std::puts("PASS: input FIFO, focus/quit/reset/window barriers, disabled mode, device/window isolation, count/time bounds"); +} diff --git a/terminal/moonlight/tests/test_latency.cpp b/terminal/moonlight/tests/test_latency.cpp new file mode 100644 index 0000000..0eb2a74 --- /dev/null +++ b/terminal/moonlight/tests/test_latency.cpp @@ -0,0 +1,75 @@ +#include +#include +#include "vdi_mouse_batch.h" +#include "vdi_latest_frame.h" +#undef NDEBUG +#include + +static SDL_Event motion(int x, Uint32 which = 1) +{ + SDL_Event event{}; + event.type = SDL_MOUSEMOTION; + event.motion.which = which; + event.motion.windowID = 1; + event.motion.x = x; + event.motion.y = 100; + event.motion.xrel = 1; + return event; +} + +int main() +{ + assert(SDL_Init(SDL_INIT_EVENTS) == 0); + for (Uint32 boundary : {Uint32(SDL_MOUSEBUTTONDOWN), Uint32(SDL_MOUSEBUTTONUP), + Uint32(SDL_KEYDOWN), Uint32(SDL_USEREVENT)}) { + SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); + SDL_Event first = motion(10), adjacent = motion(20), later = motion(90), barrier{}; + barrier.type = boundary; + assert(SDL_PushEvent(&adjacent) == 1); + assert(SDL_PushEvent(&barrier) == 1); + assert(SDL_PushEvent(&later) == 1); + vdiBatchMouseMotion(first.motion); + assert(first.motion.x == 20 && first.motion.xrel == 2); + SDL_Event next; + assert(SDL_PeepEvents(&next, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1); + assert(next.type == boundary); + assert(SDL_PeepEvents(&next, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 1); + assert(next.motion.x == 90); + } + SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); + SDL_Event first = motion(0); + for (int i = 1; i <= 200; ++i) { + SDL_Event next = motion(i); + assert(SDL_PushEvent(&next) == 1); + } + vdiBatchMouseMotion(first.motion); + assert(first.motion.x == 64); // Work per dispatch is bounded. + assert(SDL_PeepEvents(nullptr, 0, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) == 136); + SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); + first = motion(10); + SDL_Event synthetic = motion(99, SDL_TOUCH_MOUSEID); + assert(SDL_PushEvent(&synthetic) == 1); + vdiBatchMouseMotion(first.motion); + assert(first.motion.x == 10); + + AVFrame* presented = av_frame_alloc(); + assert(presented); + presented->buf[0] = av_buffer_alloc(16); + assert(presented->buf[0]); + QQueue queue; + assert(vdiDiscardQueuedFrames(queue) == 0); + for (int i = 0; i < 200; ++i) { + unsigned dropped = vdiDiscardQueuedFrames(queue); + assert(dropped == (i == 0 ? 0u : 1u)); + AVFrame* frame = av_frame_alloc(); + assert(frame); + frame->pts = i; + queue.enqueue(frame); + assert(queue.size() == 1 && queue.head()->pts == i); + assert(av_buffer_get_ref_count(presented->buf[0]) == 1); + } + assert(vdiDiscardQueuedFrames(queue) == 1); + av_frame_free(&presented); + SDL_Quit(); + std::puts("PASS: SDL event boundaries, bounded motion work, latest decoded frame ownership"); +} diff --git a/tools/bench-mouse-stream.sh b/tools/bench-mouse-stream.sh new file mode 100644 index 0000000..fa6c4b5 --- /dev/null +++ b/tools/bench-mouse-stream.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Temporary benchmark stream only. Refuses to replace an existing user stream. +# Run after launching the scoped Windows input benchmark, with VDD mode matched. +set -euo pipefail +task_root="${VDI_RKMPP_BUILD_ROOT:-$HOME/vdi-build/rkmpp-experiment}" +label="${1:?label required}" +fps="${2:?FPS required}" +priority="${3:-0}" +[[ "$label" =~ ^[a-zA-Z0-9-]+$ ]] || exit 2 +[[ "$fps" = 30 || "$fps" = 60 ]] || exit 2 +[[ "$priority" = 0 || "$priority" = 1 ]] || exit 2 +exec 9>"$task_root/mouse-bench.lock" +flock -n 9 || { echo 'Another benchmark is active'; exit 1; } +if pgrep -f '^/home/armbian/vdi-build/rkmpp-experiment/build/moonlight/app/moonlight.* stream ' >/dev/null; then + echo 'A stream is already active; exit it normally before benchmarking' >&2 + exit 1 +fi +export DISPLAY=:0 XAUTHORITY="$HOME/.Xauthority" +export LD_LIBRARY_PATH="$task_root/runtime/lib:$task_root/deps/usr/lib/aarch64-linux-gnu" +export QML2_IMPORT_PATH="$task_root/deps/usr/lib/aarch64-linux-gnu/qt5/qml" +export QT_PLUGIN_PATH="$task_root/deps/usr/lib/aarch64-linux-gnu/qt5/plugins" +export H264_DECODER_HINT=h264_rkmpp HEVC_DECODER_HINT=hevc_rkmpp +export VDI_LATEST_FRAME=1 VDI_INPUT_TIMING=1 VDI_INPUT_PRIORITY="$priority" +export GL_IS_SLOW=0 SDL_RENDER_DRIVER=opengles2 SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR=1 +client="${VDI_RKMPP_CLIENT:-$task_root/build/moonlight/app/moonlight}" +mkdir -p "$task_root/mouse-bench" +log="$task_root/mouse-bench/$label.log" +test ! -e "$log" || { echo 'Label already used; preserve existing evidence'; exit 1; } +child="" +cleanup() { + if [ -n "$child" ] && kill -0 "$child" 2>/dev/null; then + kill -TERM "$child" 2>/dev/null || true + for ((j=0;j<5;j++)); do + kill -0 "$child" 2>/dev/null || break + sleep 1 + done + kill -KILL "$child" 2>/dev/null || true + wait "$child" 2>/dev/null || true + fi +} +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM +"$client" stream 192.168.1.70 Desktop --resolution 2560x1440 --fps "$fps" \ + --bitrate 30000 --display-mode fullscreen --video-codec H.264 \ + --video-decoder hardware --absolute-mouse --keep-awake --no-vsync \ + --no-frame-pacing --audio-on-host > "$log" 2>&1 & +child=$! +ready=0 +for ((i=0;i<35;i++)); do + if grep -q 'egl_overlay.frag' "$log"; then ready=1; break; fi + kill -0 "$child" || { tail -20 "$log"; exit 1; } + sleep 1 +done +test "$ready" = 1 || { echo 'No video readiness'; exit 1; } +echo "Ready: $label; injecting motion" +python3 "$task_root/mouse-bench/inject-mouse-sweep.py" +python3 "$task_root/mouse-bench/inject-input-burst.py" +sleep 12 +# Do not send the exit chord into an unrelated foreground application. +focus_pid=$(xdotool getwindowfocus getwindowpid 2>/dev/null || true) +if [ "$focus_pid" != "$child" ]; then + echo 'Stream lost foreground; terminate only benchmark child' >&2 + exit 1 +fi +xdotool key ctrl+alt+shift+q +for ((i=0;i<15;i++)); do + kill -0 "$child" 2>/dev/null || break + sleep 1 +done +kill -0 "$child" 2>/dev/null && { echo 'Stream exit timed out'; exit 1; } +wait "$child" +child="" +grep -E 'VDI input|VDI main-thread|frame rate|Host processing|Frames dropped|Average .*time|Average network|queue delay' "$log" diff --git a/tools/build-rkmpp-client.sh b/tools/build-rkmpp-client.sh new file mode 100644 index 0000000..f53f39e --- /dev/null +++ b/tools/build-rkmpp-client.sh @@ -0,0 +1,166 @@ +#!/bin/bash +# Run on the ARM64 terminal. All sources, libraries and output stay in TASK_ROOT. +# No apt install, system library replacement, desktop restart or sudo is needed. +set -euo pipefail +TASK_ROOT="${VDI_RKMPP_BUILD_ROOT:-$HOME/vdi-build/rkmpp-experiment}" +SDK="$TASK_ROOT/deps/usr" +PREFIX="$TASK_ROOT/runtime" +JOBS="${VDI_BUILD_JOBS:-2}" +mkdir -p "$TASK_ROOT"/{src,debs,build} "$PREFIX" + +clone_source() { + local name="$1" ref="$2" url="$3" + if [ ! -d "$TASK_ROOT/src/$name/.git" ]; then + git clone --depth=1 --branch "$ref" "$url" "$TASK_ROOT/src/$name" + fi + test "$(git -C "$TASK_ROOT/src/$name" rev-parse HEAD)" = "$4" || { + echo "Unexpected $name source revision; inspect before building" >&2; exit 1; + } +} +clone_source mpp develop https://github.com/rockchip-linux/mpp.git 7916c3cc000fefc0bdf96636b7d54fe174724f44 +clone_source ffmpeg 6.1 https://github.com/nyanmisaka/ffmpeg-rockchip.git 797387de1c9caa9170100d7217ee093d1897f750 +clone_source moonlight v6.2.0 https://github.com/moonlight-stream/moonlight-qt.git de2467e433821664cdd2224aad8c89a625be1ad9 +git -C "$TASK_ROOT/src/moonlight" submodule update --init --recursive --depth=1 + +# Versioned VDI interaction patches. Explicit directory supports copying this +# builder to the box without the rest of the repository. Refuse unknown drift. +PATCH_DIR="${VDI_MOONLIGHT_PATCH_DIR:-$(cd "$(dirname "$0")/../terminal/moonlight/patches" && pwd)}" +apply_vdi_patch() { + local source="$1" patch="$2" + test -r "$patch" || { echo "Missing VDI patch: $patch" >&2; exit 1; } + if git -C "$source" apply --reverse --check "$patch" 2>/dev/null; then + return + fi + git -C "$source" apply --check "$patch" + git -C "$source" apply "$patch" +} +apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0001-vdi-input-and-latest-frame.patch" +apply_vdi_patch "$TASK_ROOT/src/moonlight/moonlight-common-c/moonlight-common-c" \ + "$PATCH_DIR/0002-vdi-absolute-click-barrier.patch" +apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0003-vdi-input-priority.patch" + +# Moonlight 6.2.0 uses std::optional in drm.h but relies on a transitive +# include from Qt 6. Our Qt 5 build needs the standard header explicitly. +export VDI_MOONLIGHT_SOURCE="$TASK_ROOT/src/moonlight" +python3 - <<'PY' +import os +from pathlib import Path +header = Path(os.environ['VDI_MOONLIGHT_SOURCE']) / 'app/streaming/video/ffmpeg-renderers/drm.h' +text = header.read_text() +if '#include ' not in text: + assert '#include ' in text + header.write_text(text.replace('#include ', '#include \n#include ', 1)) +PY + +# Download only the required headers and private runtime libraries from the +# configured distribution repository. apt does not install or upgrade anything. +cd "$TASK_ROOT/debs" +apt-get download libdrm-dev libopus-dev libsdl2-dev libsdl2-2.0-0 \ + libsdl2-ttf-dev libsdl2-ttf-2.0-0 libudev-dev libxkbcommon-dev \ + libegl-dev libgles-dev libgles1 libgbm-dev libwayland-dev libwayland-bin wayland-protocols \ + qtdeclarative5-dev qtdeclarative5-dev-tools qt5-qmltooling-plugins \ + qtquickcontrols2-5-dev libqt5quickcontrols2-5 libqt5quicktemplates2-5 \ + libqt5svg5-dev libqt5svg5 libqt5qmlworkerscript5 libqt5quickparticles5 \ + libqt5quickshapes5 libqt5quicktest5 libx264-dev \ + qml-module-qtquick2 qml-module-qtquick-window2 qml-module-qtquick-layouts \ + qml-module-qtqml-models2 qml-module-qtquick-controls2 qml-module-qtquick-templates2 \ + libharfbuzz-dev libfreetype-dev libpng-dev zlib1g-dev libbrotli-dev \ + libglib2.0-dev libgraphite2-dev libffi-dev +for package in ./*.deb; do dpkg-deb -x "$package" "$TASK_ROOT/deps"; done + +# Relocate pkg-config prefixes, preserving the system /usr and its libraries. +export VDI_PRIVATE_SDK="$SDK" +python3 - <<'PY' +import os +from pathlib import Path +sdk = Path(os.environ['VDI_PRIVATE_SDK']) +for pc in sdk.rglob('*.pc'): + text = pc.read_text() + if str(sdk) not in text: + pc.write_text(text.replace('/usr', str(sdk))) +for pri in sdk.rglob('qt_lib_*.pri'): + pri.write_text(pri.read_text().replace('$$QT_MODULE_LIB_BASE', str(sdk / 'lib/aarch64-linux-gnu')) + .replace('$$QT_MODULE_INCLUDE_BASE', str(sdk / 'include/aarch64-linux-gnu/qt5')) + .replace('$$QT_MODULE_BIN_BASE', str(sdk / 'lib/qt5/bin'))) +# A -dev symlink can target a different distro patch version than is installed. +# Link to the existing matching SONAME; never replace the system runtime. +for link in (sdk / 'lib/aarch64-linux-gnu').glob('*.so'): + if link.is_symlink() and not link.exists(): + target = Path(os.readlink(link)).name + if '.so.' not in target: + continue + soname = target.split('.so.')[0] + '.so.' + target.split('.so.')[1].split('.')[0] + system_lib = Path('/lib/aarch64-linux-gnu') / soname + if system_lib.exists(): + link.unlink() + link.symlink_to(system_lib) +PY +export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$SDK/lib/aarch64-linux-gnu/pkgconfig:$SDK/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" +export LD_LIBRARY_PATH="$PREFIX/lib:$SDK/lib/aarch64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +export QMAKEPATH="$SDK/lib/aarch64-linux-gnu/qt5" +export QML2_IMPORT_PATH="$SDK/lib/aarch64-linux-gnu/qt5/qml" +export CPLUS_INCLUDE_PATH="$SDK/include/aarch64-linux-gnu:$SDK/include${CPLUS_INCLUDE_PATH:+:$CPLUS_INCLUDE_PATH}" +export C_INCLUDE_PATH="$SDK/include/aarch64-linux-gnu:$SDK/include${C_INCLUDE_PATH:+:$C_INCLUDE_PATH}" + +if [ "${VDI_BUILD_STAGE:-all}" != moonlight ]; then +echo '=== build MPP ===' +cmake -S "$TASK_ROOT/src/mpp" -B "$TASK_ROOT/build/mpp" \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DRKPLATFORM=ON -DHAVE_DRM=ON -DBUILD_SHARED_LIBS=ON -DBUILD_TEST=OFF +cmake --build "$TASK_ROOT/build/mpp" -j "$JOBS" +cmake --install "$TASK_ROOT/build/mpp" + +echo '=== build FFmpeg with RKMPP ===' +mkdir -p "$TASK_ROOT/build/ffmpeg" +cd "$TASK_ROOT/build/ffmpeg" +"$TASK_ROOT/src/ffmpeg/configure" --prefix="$PREFIX" \ + --enable-gpl --enable-version3 --enable-shared --disable-static \ + --enable-libdrm --enable-rkmpp --disable-rkrga --enable-libx264 \ + --disable-doc --disable-debug --disable-autodetect --disable-network \ + --disable-everything --enable-avcodec --enable-avformat --enable-avfilter \ + --enable-swscale --enable-swresample --enable-ffmpeg --enable-ffprobe \ + --enable-decoder=h264,hevc,h264_rkmpp,hevc_rkmpp,rawvideo,wrapped_avframe \ + --enable-parser=h264,hevc --enable-demuxer=h264,hevc \ + --enable-muxer=h264,null,rawvideo --enable-protocol=file \ + --enable-encoder=libx264,wrapped_avframe,rawvideo --enable-indev=lavfi \ + --enable-filter=testsrc2,null,format,hwdownload,scale \ + --extra-cflags="-I$PREFIX/include -I$SDK/include" \ + --extra-ldflags="-L$PREFIX/lib -L$SDK/lib/aarch64-linux-gnu -Wl,-rpath,$PREFIX/lib -Wl,-rpath,$SDK/lib/aarch64-linux-gnu" +make -j "$JOBS" +make install +"$PREFIX/bin/ffmpeg" -hide_banner -decoders | grep rkmpp +fi + +echo '=== build Moonlight ===' +mkdir -p "$TASK_ROOT/build/moonlight" +cd "$TASK_ROOT/build/moonlight" +qmake "$TASK_ROOT/src/moonlight/moonlight-qt.pro" \ + CONFIG+=release CONFIG+=recheck CONFIG+=disable-libva CONFIG+=disable-libvdpau \ + CONFIG+=disable-libplacebo CONFIG+=disable-discord \ + "QMAKE_LIBDIR+=$SDK/lib/aarch64-linux-gnu" \ + "QMAKE_LIBDIR+=$PREFIX/lib" \ + "QMAKE_RPATHDIR+=$SDK/lib/aarch64-linux-gnu" \ + "QMAKE_RPATHDIR+=$PREFIX/lib" \ + "QMAKE_INCDIR+=$SDK/include/aarch64-linux-gnu/qt5" \ + "QMAKE_INCDIR+=$SDK/include/aarch64-linux-gnu" \ + "QMAKE_INCDIR+=$SDK/include" +make qmake_all +if ! grep -q -- '-DHAVE_EGL' app/Makefile.Release; then + echo 'EGL renderer missing; refusing to build a client without the DRM PRIME display path' >&2 + exit 1 +fi +# qmake/make does not invalidate old objects when feature defines change. +# In particular a stale ffmpeg.o can silently omit EGL despite a successful +# configure check. Clean the app whenever its compiler/link flags change. +flags_hash="$(grep -E '^(DEFINES|INCPATH|LIBS|CXXFLAGS)[[:space:]]*=' app/Makefile.Release | sha256sum | cut -d ' ' -f 1)" +if [ "$(cat .vdi-build-flags.sha256 2>/dev/null || true)" != "$flags_hash" ]; then + make -C app -f Makefile.Release clean + printf '%s\n' "$flags_hash" > .vdi-build-flags.sha256 +fi +make -j "$JOBS" +echo '=== complete; experimental binary only, production unchanged ===' +echo "$TASK_ROOT/build/moonlight/app/moonlight" +git -C "$TASK_ROOT/src/mpp" rev-parse HEAD +git -C "$TASK_ROOT/src/ffmpeg" rev-parse HEAD +git -C "$TASK_ROOT/src/moonlight" rev-parse HEAD +sha256sum "$TASK_ROOT"/debs/*.deb > "$TASK_ROOT/dependency-sha256.txt" diff --git a/tools/deploy_display_guard.sh b/tools/deploy_display_guard.sh new file mode 100644 index 0000000..172eb09 --- /dev/null +++ b/tools/deploy_display_guard.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Install only X11 display recovery; keep the kiosk/Moonlight session running. +set -euo pipefail + +BOX="${VDI_BOX:-armbian@192.168.1.188}" +REPO="$(cd "$(dirname "$0")/.." && pwd)" +SRC="$REPO/terminal/kiosk-shell" +STAGE=$(ssh -o BatchMode=yes -o ConnectTimeout=8 "$BOX" 'mktemp -d /tmp/vdi-display-deploy.XXXXXX') +scp -q "$SRC/scripts/vdi-display-guard" "$SRC/scripts/vdi-kiosk-start" \ + "$SRC/etc/vdi-display-guard.service" "$BOX:$STAGE/" +ssh "$BOX" bash -s -- "$STAGE" <<'REMOTE' +set -euo pipefail +stage=$1 +backup="$HOME/vdi-terminal/backups/display-guard-$(date +%Y%m%d-%H%M%S)" +mkdir -p "$backup" "$HOME/vdi-terminal/bin" "$HOME/.config/systemd/user" +for file in "$HOME/vdi-terminal/bin/vdi-kiosk-start" \ + "$HOME/vdi-terminal/bin/vdi-display-guard" \ + "$HOME/.config/systemd/user/vdi-display-guard.service"; do + test ! -f "$file" || cp -p "$file" "$backup/" +done +sed -i 's/\r$//' "$stage/"* +python3 -c 'import ast, sys; ast.parse(open(sys.argv[1]).read())' "$stage/vdi-display-guard" +sh -n "$stage/vdi-kiosk-start" +install -m 0755 "$stage/vdi-display-guard" "$stage/vdi-kiosk-start" "$HOME/vdi-terminal/bin/" +install -m 0644 "$stage/vdi-display-guard.service" "$HOME/.config/systemd/user/" + +# Correct only this monitor's known stale layout; preserve all other layouts. +python3 - "$backup" <<'PY' +from pathlib import Path +import shutil +import sys +import xml.etree.ElementTree as ET +path = Path.home() / '.config/monitors.xml' +if path.exists(): + tree = ET.parse(path) + changed = False + for output in tree.findall('.//output'): + if (output.get('name') == 'HDMI-1' + and output.findtext('vendor') == 'GWR' + and output.findtext('product') == '0x2708' + and output.findtext('width') == '1920' + and output.findtext('height') == '1440' + and output.findtext('rate') == '75'): + for name, value in [('width', '2560'), ('height', '1440'), ('rate', '60')]: + output.find(name).text = value + changed = True + if changed: + shutil.copy2(path, Path(sys.argv[1]) / 'monitors.xml') + temp = path.with_name('monitors.xml.vdi-new') + tree.write(temp, encoding='utf-8', xml_declaration=True) + temp.replace(path) + print('Updated known stale MATE layout to 2560x1440@60') +PY +systemctl --user daemon-reload +systemctl --user enable vdi-display-guard.service +systemctl --user restart vdi-display-guard.service +systemctl --user is-active vdi-display-guard.service +echo "Backup: $backup" +# Only remove the three files uploaded by this deployment. +rm -- "$stage/vdi-display-guard" "$stage/vdi-kiosk-start" "$stage/vdi-display-guard.service" +rmdir -- "$stage" +REMOTE diff --git a/tools/deploy_kiosk.sh b/tools/deploy_kiosk.sh index 13192e6..c5b0ed2 100644 --- a/tools/deploy_kiosk.sh +++ b/tools/deploy_kiosk.sh @@ -15,21 +15,30 @@ 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 ~/vdi-build/repo/kiosk-shell/tests && mkdir -p ~/vdi-build/repo/kiosk-shell/src ~/vdi-build/repo/kiosk-shell/tests' +ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'rm -rf ~/vdi-build/repo/kiosk-shell/src ~/vdi-build/repo/kiosk-shell/tests && mkdir -p ~/vdi-build/repo/kiosk-shell/src ~/vdi-build/repo/kiosk-shell/tests ~/vdi-build/repo/kiosk-shell/scripts' 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"/tests/*.cpp "$BOX":~/vdi-build/repo/kiosk-shell/tests/ +scp -q "$SRC/tests/test_display_guard.py" "$BOX":~/vdi-build/repo/kiosk-shell/tests/ +scp -q "$SRC/scripts/vdi-display-guard" "$BOX":~/vdi-build/repo/kiosk-shell/scripts/ scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/scripts/vdi-moonlight" "$SRC/scripts/vdi-kiosk-start" "$BOX":~/vdi-terminal/bin/ +scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/scripts/vdi-moonlight-rkmpp" "$BOX":~/vdi-terminal/bin/ +ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'sed -i "s/\r$//" ~/vdi-terminal/bin/vdi-moonlight-rkmpp; chmod +x ~/vdi-terminal/bin/vdi-moonlight-rkmpp' +scp -q "$SRC/etc/70-vdi-rockchip-video.rules" "$BOX":/tmp/70-vdi-rockchip-video.rules +ssh "$BOX" 'sed -i "s/\r$//" /tmp/70-vdi-rockchip-video.rules; sudo -n install -m 0644 /tmp/70-vdi-rockchip-video.rules /etc/udev/rules.d/70-vdi-rockchip-video.rules && sudo -n udevadm control --reload-rules && for device in /dev/mpp_service /dev/rga /dev/dma_heap/*; do test ! -e "$device" || { sudo -n chgrp video "$device" && sudo -n chmod 0660 "$device"; } || exit 1; done' scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$SRC/etc/moonlight.conf" "$BOX":/tmp/vdi-moonlight.conf ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'mkdir -p ~/.config/vdi-terminal; test -f ~/.config/vdi-terminal/moonlight.conf || cp /tmp/vdi-moonlight.conf ~/.config/vdi-terminal/moonlight.conf; rm -f /tmp/vdi-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 "==> 安装 HDMI 热插拔恢复服务" +bash "$REPO/tools/deploy_display_guard.sh" + 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-build/repo/kiosk-shell/tests/*.cpp ~/vdi-terminal/bin/vdi-moonlight ~/vdi-terminal/bin/vdi-kiosk-start; chmod +x ~/vdi-terminal/bin/vdi-moonlight ~/vdi-terminal/bin/vdi-kiosk-start; cd ~/vdi-build/repo/kiosk-shell && (cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON > /tmp/cmake.log 2>&1 || (tail -30 /tmp/cmake.log; exit 1)) && cmake --build build && QT_QPA_PLATFORM=offscreen ctest --test-dir build --output-on-failure' echo "==> 安装二进制" -ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'monitor_pids=$(pgrep -f "^/home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- " 2>/dev/null || true); test -z "$monitor_pids" || kill $monitor_pids 2>/dev/null || true; pair_pids=$(pgrep -f "^bash -c tail -f .*(pin|pair)_fifo" 2>/dev/null || true); test -z "$pair_pids" || kill $pair_pids 2>/dev/null || true; tail_pids=$(pgrep -f "^tail -f .*(pin|pair)_fifo" 2>/dev/null || true); test -z "$tail_pids" || kill $tail_pids 2>/dev/null || true; rm -f ~/pair_fifo /tmp/pin_fifo; for name in vdi-moonlight moonlight-qt kiosk-shell; do pids=$(pidof "$name" 2>/dev/null || true); test -z "$pids" || kill $pids 2>/dev/null || true; done; for i in $(seq 1 40); do pgrep -f "^/home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- " >/dev/null 2>&1 || break; sleep 0.2; done; monitor_pids=$(pgrep -f "^/home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- " 2>/dev/null || true); test -z "$monitor_pids" || kill -9 $monitor_pids 2>/dev/null || true; 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/' +ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$BOX" 'monitor_pids=$(pgrep -f "^/home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- " 2>/dev/null || true); test -z "$monitor_pids" || kill $monitor_pids 2>/dev/null || true; pair_pids=$(pgrep -f "^bash -c tail -f .*(pin|pair)_fifo" 2>/dev/null || true); test -z "$pair_pids" || kill $pair_pids 2>/dev/null || true; tail_pids=$(pgrep -f "^tail -f .*(pin|pair)_fifo" 2>/dev/null || true); test -z "$tail_pids" || kill $tail_pids 2>/dev/null || true; rm -f ~/pair_fifo /tmp/pin_fifo; for name in vdi-moonlight moonlight-qt moonlight kiosk-shell; do pids=$(pidof "$name" 2>/dev/null || true); test -z "$pids" || kill $pids 2>/dev/null || true; done; for i in $(seq 1 40); do pgrep -f "^/home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- " >/dev/null 2>&1 || break; sleep 0.2; done; monitor_pids=$(pgrep -f "^/home/armbian/vdi-terminal/bin/vdi-kiosk-monitor -- " 2>/dev/null || true); test -z "$monitor_pids" || kill -9 $monitor_pids 2>/dev/null || true; 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' diff --git a/tools/generate-h264-sample.c b/tools/generate-h264-sample.c new file mode 100644 index 0000000..b1ddc40 --- /dev/null +++ b/tools/generate-h264-sample.c @@ -0,0 +1,54 @@ +// Generate a deterministic 1440p H.264 clip without any display/capture dependency. +// Link against the isolated FFmpeg libavcodec/libavutil built for the terminal. +#include +#include +#include +#include + +static int write_packets(AVCodecContext *encoder, AVPacket *packet, FILE *output) { + int result; + while ((result = avcodec_receive_packet(encoder, packet)) == 0) { + if (fwrite(packet->data, 1, packet->size, output) != (size_t)packet->size) return -1; + av_packet_unref(packet); + } + return result == AVERROR(EAGAIN) || result == AVERROR_EOF ? 0 : result; +} + +int main(int argc, char **argv) { + if (argc != 2) return 2; + const AVCodec *codec = avcodec_find_encoder_by_name("libx264"); + AVCodecContext *encoder = avcodec_alloc_context3(codec); + AVFrame *frame = av_frame_alloc(); + AVPacket *packet = av_packet_alloc(); + FILE *output = fopen(argv[1], "wb"); + if (!codec || !encoder || !frame || !packet || !output) return 1; + encoder->width = frame->width = 2560; + encoder->height = frame->height = 1440; + encoder->pix_fmt = frame->format = AV_PIX_FMT_YUV420P; + encoder->time_base = (AVRational){1, 30}; + encoder->framerate = (AVRational){30, 1}; + encoder->max_b_frames = 0; + encoder->thread_count = 2; + av_opt_set(encoder->priv_data, "preset", "ultrafast", 0); + av_opt_set(encoder->priv_data, "tune", "zerolatency", 0); + if (avcodec_open2(encoder, codec, NULL) < 0 || av_frame_get_buffer(frame, 32) < 0) return 1; + for (int i = 0; i < 60; i++) { + if (av_frame_make_writable(frame) < 0) return 1; + for (int y = 0; y < frame->height; y++) { + memset(frame->data[0] + y * frame->linesize[0], 32 + (y + i * 4) % 190, frame->width); + } + for (int plane = 1; plane < 3; plane++) { + for (int y = 0; y < frame->height / 2; y++) { + memset(frame->data[plane] + y * frame->linesize[plane], 128, frame->width / 2); + } + } + frame->pts = i; + if (avcodec_send_frame(encoder, frame) < 0 || write_packets(encoder, packet, output) < 0) return 1; + } + if (avcodec_send_frame(encoder, NULL) < 0 || write_packets(encoder, packet, output) < 0) return 1; + if (fclose(output) != 0) return 1; + av_packet_free(&packet); + av_frame_free(&frame); + avcodec_free_context(&encoder); + return 0; +} diff --git a/tools/inject-input-burst.py b/tools/inject-input-burst.py new file mode 100644 index 0000000..41ebcc8 --- /dev/null +++ b/tools/inject-input-burst.py @@ -0,0 +1,26 @@ +"""X11 test only: inject 32 alternating left/right clicks into the bench window.""" +import ctypes + +x11 = ctypes.CDLL("libX11.so.6") +xtest = ctypes.CDLL("libXtst.so.6") +x11.XOpenDisplay.argtypes = [ctypes.c_char_p] +x11.XOpenDisplay.restype = ctypes.c_void_p +x11.XFlush.argtypes = [ctypes.c_void_p] +x11.XCloseDisplay.argtypes = [ctypes.c_void_p] +xtest.XTestFakeMotionEvent.argtypes = [ctypes.c_void_p, ctypes.c_int, + ctypes.c_int, ctypes.c_int, ctypes.c_ulong] +xtest.XTestFakeButtonEvent.argtypes = [ctypes.c_void_p, ctypes.c_uint, + ctypes.c_int, ctypes.c_ulong] +display = x11.XOpenDisplay(None) +if not display: + raise RuntimeError("X11 unavailable") +try: + for index in range(32): + x = 640 if index % 2 == 0 else 1920 + assert xtest.XTestFakeMotionEvent(display, -1, x, 600, 0) + assert xtest.XTestFakeButtonEvent(display, 1, 1, 0) + assert xtest.XTestFakeButtonEvent(display, 1, 0, 0) + x11.XFlush(display) +finally: + x11.XCloseDisplay(display) +print("Injected 32 clicks, expected L,R alternating; final pointer on R") diff --git a/tools/inject-mouse-sweep.py b/tools/inject-mouse-sweep.py new file mode 100644 index 0000000..a223afd --- /dev/null +++ b/tools/inject-mouse-sweep.py @@ -0,0 +1,31 @@ +"""X11 benchmark only: 20 seconds of 125 Hz motion, no buttons or keys.""" +import ctypes +import math +import time + +x11 = ctypes.CDLL("libX11.so.6") +xtest = ctypes.CDLL("libXtst.so.6") +x11.XOpenDisplay.argtypes = [ctypes.c_char_p] +x11.XOpenDisplay.restype = ctypes.c_void_p +x11.XFlush.argtypes = [ctypes.c_void_p] +x11.XCloseDisplay.argtypes = [ctypes.c_void_p] +xtest.XTestFakeMotionEvent.argtypes = [ctypes.c_void_p, ctypes.c_int, + ctypes.c_int, ctypes.c_int, ctypes.c_ulong] +display = x11.XOpenDisplay(None) +if not display: + raise RuntimeError("X11 unavailable") +start = time.monotonic() +count = 0 +try: + while time.monotonic() - start < 20: + elapsed = time.monotonic() - start + x = round(1280 + 1000 * math.sin(elapsed * 4)) + y = round(720 + 450 * math.cos(elapsed * 3)) + if not xtest.XTestFakeMotionEvent(display, -1, x, y, 0): + raise RuntimeError("Motion injection failed") + x11.XFlush(display) + count += 1 + time.sleep(max(0, start + count / 125 - time.monotonic())) +finally: + x11.XCloseDisplay(display) +print(f"Injected {count} motion events in {time.monotonic()-start:.2f}s") diff --git a/tools/input-latency-bench.ps1 b/tools/input-latency-bench.ps1 new file mode 100644 index 0000000..d9d6b98 --- /dev/null +++ b/tools/input-latency-bench.ps1 @@ -0,0 +1,85 @@ +# Temporary interactive test window; exits after ten minutes. No system settings. +$ErrorActionPreference = 'Stop' +$benchDir = 'C:\ProgramData\VDI\input-bench-20261103' +New-Item -ItemType Directory -Force $benchDir | Out-Null +Set-Content -LiteralPath "$benchDir\pid.txt" -Value $PID +Add-Type -ReferencedAssemblies System.Windows.Forms,System.Drawing -TypeDefinition @' +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; +public class VDIInputBenchForm : Form { + [DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); + [DllImport("winmm.dll")] public static extern uint timeBeginPeriod(uint period); + [DllImport("winmm.dll")] public static extern uint timeEndPeriod(uint period); + readonly Stopwatch watch = Stopwatch.StartNew(); + readonly Timer timer = new Timer(); + readonly string folder; + int paints, clicks; + long lastReport; + long lastFrameIndex = -1; + public VDIInputBenchForm(string path) { + folder = path; + Text = "VDI Input Latency Bench"; + FormBorderStyle = FormBorderStyle.None; + Bounds = Screen.PrimaryScreen.Bounds; + TopMost = true; + DoubleBuffered = true; + File.WriteAllText(Path.Combine(folder,"clicks.csv"), "ms,x,y,target\n"); + // A WinForms 16ms timer can quantize to ~31ms at the default timer + // resolution and accidentally cap the *source* at 32FPS. Pace by an + // absolute monotonic frame index, not accumulated timer ticks. + timer.Interval = 1; + timer.Tick += delegate { + if(watch.Elapsed.TotalMinutes >= 10) { Close(); return; } + long frameIndex = watch.ElapsedTicks * 60 / Stopwatch.Frequency; + if(frameIndex != lastFrameIndex) { lastFrameIndex = frameIndex; Invalidate(); } + }; + timer.Start(); + } + protected override void OnMouseDown(MouseEventArgs e) { + base.OnMouseDown(e); + string target = e.X < Width/2 ? "L" : "R"; + File.AppendAllText(Path.Combine(folder,"clicks.csv"), + watch.ElapsedMilliseconds+","+e.X+","+e.Y+","+target+"\n"); + ++clicks; + Invalidate(); + } + protected override void OnPaint(PaintEventArgs e) { + base.OnPaint(e); + long ms = watch.ElapsedMilliseconds; + e.Graphics.Clear(Color.FromArgb(16,24,39)); + using(Brush left = new SolidBrush(Color.FromArgb(35,85,130))) + using(Brush right = new SolidBrush(Color.FromArgb(50,115,90))) { + e.Graphics.FillRectangle(left,50,180,Width/2-100,Height-260); + e.Graphics.FillRectangle(right,Width/2+50,180,Width/2-100,Height-260); + } + int x = (int)((ms*0.6)%(Width+100))-100; + e.Graphics.FillRectangle(Brushes.Gold,x,0,60,Height); + using(Font font = new Font("Consolas",24)) { + e.Graphics.DrawString("VDI test: moving bars + ordered clicks | "+clicks+" clicks | "+ms+" ms", + font,Brushes.White,70,40); + e.Graphics.DrawString("LEFT",font,Brushes.White,Width/4-50,Height/2); + e.Graphics.DrawString("RIGHT",font,Brushes.White,Width*3/4-50,Height/2); + } + ++paints; + if(ms-lastReport>=1000) { + File.WriteAllText(Path.Combine(folder,"status.txt"), + "width="+Width+" height="+Height+" paints="+paints+" elapsed_ms="+ms+" clicks="+clicks); + lastReport=ms; + } + } + protected override void OnFormClosed(FormClosedEventArgs e) { + timer.Stop(); timer.Dispose(); base.OnFormClosed(e); + } +} +'@ +[VDIInputBenchForm]::SetProcessDPIAware() | Out-Null +$timerResult=[VDIInputBenchForm]::timeBeginPeriod(1) +try { + [System.Windows.Forms.Application]::Run((New-Object VDIInputBenchForm($benchDir))) +} finally { + if($timerResult -eq 0) { [VDIInputBenchForm]::timeEndPeriod(1) | Out-Null } +} diff --git a/tools/probe-nvidia-d3d.cs b/tools/probe-nvidia-d3d.cs new file mode 100644 index 0000000..cfabc6e --- /dev/null +++ b/tools/probe-nvidia-d3d.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace VdiDiagnostics { + public class AdapterResult { + public string Name; + public uint VendorId; + public int DeviceResult; + } + + // Enumerate the actual DXGI adapters and create a D3D11 device on each NVIDIA + // adapter. A WMI/PnP model name alone does not prove that D3D is operational. + public static class D3DProbe { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + private struct AdapterDesc { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string Description; + public uint VendorId, DeviceId, SubSysId, Revision; + public UIntPtr DedicatedVideoMemory, DedicatedSystemMemory, SharedSystemMemory; + public long AdapterLuid; + } + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int EnumAdapters(IntPtr factory, uint index, out IntPtr adapter); + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + private delegate int GetDesc(IntPtr adapter, out AdapterDesc desc); + [DllImport("dxgi.dll")] + private static extern int CreateDXGIFactory1(ref Guid iid, out IntPtr factory); + [DllImport("d3d11.dll")] + private static extern int D3D11CreateDevice(IntPtr adapter, uint driverType, + IntPtr software, uint flags, IntPtr levels, uint levelCount, uint sdkVersion, + out IntPtr device, out uint featureLevel, out IntPtr context); + + public static AdapterResult[] Check() { + var results = new List(); + var iid = new Guid("770aae78-f26f-4dba-a829-253c83d1b387"); + IntPtr factory; + Marshal.ThrowExceptionForHR(CreateDXGIFactory1(ref iid, out factory)); + try { + var table = Marshal.ReadIntPtr(factory); + var enumerate = (EnumAdapters)Marshal.GetDelegateForFunctionPointer( + Marshal.ReadIntPtr(table, 7 * IntPtr.Size), typeof(EnumAdapters)); + for (uint i = 0; i < 32; i++) { + IntPtr adapter; + int hr = enumerate(factory, i, out adapter); + if (hr == unchecked((int)0x887A0002)) break; + Marshal.ThrowExceptionForHR(hr); + try { + var descMethod = (GetDesc)Marshal.GetDelegateForFunctionPointer( + Marshal.ReadIntPtr(Marshal.ReadIntPtr(adapter), 8 * IntPtr.Size), typeof(GetDesc)); + AdapterDesc desc; + Marshal.ThrowExceptionForHR(descMethod(adapter, out desc)); + if (desc.VendorId != 0x10de) continue; + IntPtr device, context; + uint level; + hr = D3D11CreateDevice(adapter, 0, IntPtr.Zero, 0, IntPtr.Zero, + 0, 7, out device, out level, out context); + if (context != IntPtr.Zero) Marshal.Release(context); + if (device != IntPtr.Zero) Marshal.Release(device); + results.Add(new AdapterResult { Name = desc.Description, + VendorId = desc.VendorId, DeviceResult = hr }); + } finally { Marshal.Release(adapter); } + } + } finally { Marshal.Release(factory); } + return results.ToArray(); + } + } +} diff --git a/tools/run-input-latency-bench.py b/tools/run-input-latency-bench.py new file mode 100644 index 0000000..612c8a3 --- /dev/null +++ b/tools/run-input-latency-bench.py @@ -0,0 +1,55 @@ +"""Start/status/stop only the temporary VDI input benchmark through VM100's agent.""" +import argparse +import base64 +from pathlib import Path +import runpy +import sys + +sys.stdout.reconfigure(encoding="utf-8", errors="replace") +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument("action", choices=["start", "status", "stop"]) +args = parser.parse_args() +tools_dir = Path(__file__).resolve().parent +term = runpy.run_path(str(tools_dir / "pve_term_exec.py")) +vm = runpy.run_path(str(tools_dir / "pve_vm.py")) +ticket, csrf = term["login"]() +state = vm["api"]("/api2/json/nodes/starhe/qemu/100/status/current", ticket=ticket) +if state.get("status") != "running": + sys.exit("VM100 is not running; no benchmark task changed") +folder = r"C:\ProgramData\VDI\input-bench-20261103" +prefix = "$ErrorActionPreference='Stop'; $folder='" + folder + "'; " +if args.action == "start": + encoded = base64.b64encode((tools_dir / "input-latency-bench.ps1").read_bytes()).decode() + command = prefix + r""" +New-Item -ItemType Directory -Force $folder | Out-Null +if (Get-ScheduledTask -TaskName VDIInputLatencyBench -ErrorAction SilentlyContinue) { throw 'Benchmark task already exists; stop it first' } +[IO.File]::WriteAllBytes("$folder\bench.ps1",[Convert]::FromBase64String('""" + encoded + r"""')) +$action=New-ScheduledTaskAction -Execute powershell.exe -Argument "-NoProfile -ExecutionPolicy Bypass -STA -WindowStyle Hidden -File $folder\bench.ps1" +$principal=New-ScheduledTaskPrincipal -UserId test -LogonType Interactive -RunLevel Limited +$settings=New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 10) +Register-ScheduledTask -TaskName VDIInputLatencyBench -Action $action -Principal $principal -Settings $settings | Out-Null +Start-ScheduledTask -TaskName VDIInputLatencyBench +'Benchmark launched' +""" +elif args.action == "status": + command = prefix + r""" +Get-Content "$folder\status.txt" -ErrorAction SilentlyContinue +Get-Content "$folder\clicks.csv" -ErrorAction SilentlyContinue +Get-ScheduledTaskInfo -TaskName VDIInputLatencyBench | Select-Object LastTaskResult | ConvertTo-Json -Compress +""" +else: + command = prefix + r""" +Stop-ScheduledTask -TaskName VDIInputLatencyBench -ErrorAction SilentlyContinue +Unregister-ScheduledTask -TaskName VDIInputLatencyBench -Confirm:$false -ErrorAction SilentlyContinue +if (Test-Path "$folder\pid.txt") { + $benchPid=[int](Get-Content "$folder\pid.txt") + $process=Get-CimInstance Win32_Process -Filter "ProcessId=$benchPid" + if ($process.CommandLine -like '*input-bench-20261103*') { Stop-Process -Id $benchPid -ErrorAction SilentlyContinue } +} +'Benchmark stopped; other applications untouched' +""" +out, error = vm["agent"](ticket, csrf, ["powershell.exe", "-NoProfile", "-Command", command]) +print(out) +if error: + print(error, file=sys.stderr) + sys.exit(1) diff --git a/tools/run-mouse-display-probe.py b/tools/run-mouse-display-probe.py new file mode 100644 index 0000000..62b3ef7 --- /dev/null +++ b/tools/run-mouse-display-probe.py @@ -0,0 +1,61 @@ +"""Run the scoped VDD mode probe in VM100's already logged-in user session.""" +import argparse +import base64 +from pathlib import Path +import runpy +import sys + +sys.stdout.reconfigure(encoding="utf-8", errors="replace") +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument("action", choices=["Query", "List", "Apply", "Restore"]) +parser.add_argument("--hz", type=int, default=60) +args = parser.parse_args() +if not 24 <= args.hz <= 240: + parser.error("--hz must be in 24..240") +root = Path(__file__).resolve().parent +term = runpy.run_path(str(root / "pve_term_exec.py")) +vm = runpy.run_path(str(root / "pve_vm.py")) +ticket, csrf = term["login"]() +state = vm["api"]("/api2/json/nodes/starhe/qemu/100/status/current", ticket=ticket) +if state.get("status") != "running": + sys.exit("VM100 is not running; no task or display configuration changed") +payload = base64.b64encode((root / "set-vdi-display-mode.ps1").read_bytes()).decode() +command = r""" +$ErrorActionPreference='Stop' +$folder='C:\ProgramData\VDI\mouse-optimization' +$task='VDIMouseDisplayProbe' +if(Get-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue) { throw 'Probe task already exists' } +$user=(Get-CimInstance Win32_ComputerSystem).UserName +if(-not $user) { throw 'No interactive user' } +New-Item -ItemType Directory -Force $folder | Out-Null +$acl=Get-Acl -LiteralPath $folder +$rule=New-Object Security.AccessControl.FileSystemAccessRule($user,'Modify','ContainerInherit,ObjectInherit','None','Allow') +$acl.SetAccessRule($rule) +Set-Acl -LiteralPath $folder -AclObject $acl +[IO.File]::WriteAllBytes("$folder\mode.ps1",[Convert]::FromBase64String('__VDI_PAYLOAD__')) +$run="try { & '$folder\mode.ps1' -Action __VDI_ACTION__ -RefreshRate __VDI_HZ__ | Out-File -Encoding utf8 -Width 300 '$folder\mode-result.txt' } catch { `$_ | Out-String | Out-File -Encoding utf8 -Width 300 '$folder\mode-result.txt'; exit 1 }" +$encoded=[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($run)) +$action=New-ScheduledTaskAction -Execute powershell.exe -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -EncodedCommand $encoded" +$principal=New-ScheduledTaskPrincipal -UserId $user -LogonType Interactive -RunLevel Limited +$settings=New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 1) +Register-ScheduledTask -TaskName $task -Action $action -Principal $principal -Settings $settings | Out-Null +try { + Start-ScheduledTask -TaskName $task + Start-Sleep -Seconds 2 + for($i=0;$i -lt 20;$i++) { + if((Get-ScheduledTask -TaskName $task).State -ne 'Running') { break } + Start-Sleep -Seconds 1 + } + if((Get-ScheduledTask -TaskName $task).State -eq 'Running') { throw 'Probe timed out' } + Get-Content "$folder\mode-result.txt" + 'task_result='+(Get-ScheduledTaskInfo -TaskName $task).LastTaskResult +} finally { + Stop-ScheduledTask -TaskName $task -ErrorAction SilentlyContinue + Unregister-ScheduledTask -TaskName $task -Confirm:$false +} +""".replace("__VDI_ACTION__", args.action).replace("__VDI_HZ__", str(args.hz)).replace("__VDI_PAYLOAD__", payload) +out, error = vm["agent"](ticket, csrf, ["powershell.exe", "-NoProfile", "-Command", command]) +print(out) +if error or "task_result=0" not in out: + print(error, file=sys.stderr) + sys.exit(1) diff --git a/tools/set-desktop-responsiveness.ps1 b/tools/set-desktop-responsiveness.ps1 new file mode 100644 index 0000000..86f94da --- /dev/null +++ b/tools/set-desktop-responsiveness.ps1 @@ -0,0 +1,52 @@ +param([switch]$Restore) +# Run in the logged-in desktop user session, not the Guest Agent's Session 0. +$ErrorActionPreference = 'Stop' +$directory = Join-Path $env:LOCALAPPDATA 'VDI' +New-Item -ItemType Directory -Force $directory | Out-Null +$backup = Join-Path $directory 'desktop-responsiveness-before-20261103.json' +Add-Type @' +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +public static class VDIVisualResponse { + [StructLayout(LayoutKind.Sequential)] struct Animation { public uint size; public int enabled; } + [DllImport("user32.dll", EntryPoint="SystemParametersInfoW", SetLastError=true)] + static extern bool SpiAnimation(uint action, uint size, ref Animation data, uint flags); + [DllImport("user32.dll", EntryPoint="SystemParametersInfoW", SetLastError=true)] + static extern bool SpiGet(uint action, uint unused, out uint value, uint flags); + [DllImport("user32.dll", EntryPoint="SystemParametersInfoW", SetLastError=true)] + static extern bool SpiSet(uint action, uint value, IntPtr unused, uint flags); + static void Check(bool result) { if(!result) throw new Win32Exception(Marshal.GetLastWin32Error()); } + public static int AnimationEnabled() { + Animation data = new Animation { size=8 }; + Check(SpiAnimation(0x0048,8,ref data,0)); return data.enabled; + } + public static uint MenuDelay() { uint value; Check(SpiGet(0x006A,0,out value,0)); return value; } + public static void Set(int animation, uint delay) { + Animation data = new Animation { size=8, enabled=animation }; + Check(SpiAnimation(0x0049,8,ref data,3)); + Check(SpiSet(0x006B,delay,IntPtr.Zero,3)); + } +} +'@ +$before = @{ animation=[VDIVisualResponse]::AnimationEnabled(); menuDelay=[VDIVisualResponse]::MenuDelay() } +if ($Restore) { + $target = Get-Content -LiteralPath $backup -Raw | ConvertFrom-Json +} else { + if (-not (Test-Path -LiteralPath $backup)) { + $before | ConvertTo-Json | Set-Content -LiteralPath $backup -Encoding UTF8 + } + $target = @{ animation=0; menuDelay=100 } +} +try { + [VDIVisualResponse]::Set([int]$target.animation,[uint32]$target.menuDelay) + $after = @{ animation=[VDIVisualResponse]::AnimationEnabled(); menuDelay=[VDIVisualResponse]::MenuDelay() } + if ($after.animation -ne $target.animation -or $after.menuDelay -ne $target.menuDelay) { + throw 'Desktop preferences were not retained' + } + @{ before=$before; after=$after; backup=$backup; restored=[bool]$Restore } | + ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $directory 'desktop-responsiveness-result.json') -Encoding UTF8 +} catch { + [VDIVisualResponse]::Set([int]$before.animation,[uint32]$before.menuDelay) + throw +} diff --git a/tools/set-vdi-display-mode.ps1 b/tools/set-vdi-display-mode.ps1 new file mode 100644 index 0000000..8210a96 --- /dev/null +++ b/tools/set-vdi-display-mode.ps1 @@ -0,0 +1,111 @@ +param( + [ValidateSet('Query', 'List', 'Apply', 'Restore')][string]$Action = 'Query', + [ValidateRange(640, 7680)][int]$Width = 2560, + [ValidateRange(480, 4320)][int]$Height = 1440, + [ValidateRange(24, 240)][int]$RefreshRate = 60, + [string]$BackupPath = 'C:\ProgramData\VDI\mouse-optimization\display-before.json' +) +$ErrorActionPreference = 'Stop' +# Run in the logged-in desktop session. Apply is temporary (no registry update). +# Never assumes DISPLAY1, and refuses ambiguous active virtual displays. +Add-Type -TypeDefinition @' +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +public static class VdiModeProbe { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct Device { + public int cb; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string Name; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string Description; + public int Flags; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string Id; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string Key; + } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct Mode { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string name; + public short spec, driver, size, extra; + public int fields, x, y, orientation, fixedOutput; + public short color, duplex, yres, tt, collate; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string form; + public short logPixels; + public int bpp, width, height, flags, hz, icmMethod, icmIntent, media, dither, + reserved1, reserved2, panWidth, panHeight; + } + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + static extern bool EnumDisplayDevices(string name, uint index, ref Device dev, uint flags); + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + static extern bool EnumDisplaySettings(string name, int index, ref Mode mode); + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + static extern int ChangeDisplaySettingsEx(string name, ref Mode mode, IntPtr window, uint flags, IntPtr param); + public static string Find() { + string found = null; + for (uint i = 0; ; i++) { + var d = new Device(); d.cb = Marshal.SizeOf(typeof(Device)); + if (!EnumDisplayDevices(null, i, ref d, 0)) return found; + if ((d.Flags & 1) == 0 || d.Description.IndexOf("Virtual Display Driver", StringComparison.OrdinalIgnoreCase) < 0) continue; + if (found != null) throw new InvalidOperationException("Multiple active virtual displays; select manually"); + found = d.Name; + } + } + public static Mode Current(string name) { + var m = new Mode(); m.size = (short)Marshal.SizeOf(typeof(Mode)); + if (!EnumDisplaySettings(name, -1, ref m)) throw new InvalidOperationException("Cannot query mode"); + return m; + } + public static Mode[] List(string name) { + var modes = new List(); + for(int i=0; ; ++i) { + var m = new Mode(); m.size = (short)Marshal.SizeOf(typeof(Mode)); + if (!EnumDisplaySettings(name, i, ref m)) return modes.ToArray(); + modes.Add(m); + } + } + public static void Apply(string name, int w, int h, int hz, int bpp) { + foreach(var m0 in List(name)) { + var m = m0; + if(m.width != w || m.height != h || m.hz != hz || m.bpp != bpp) continue; + m.fields = 0x80000 | 0x100000 | 0x400000 | 0x40000; + int result = ChangeDisplaySettingsEx(name, ref m, IntPtr.Zero, 2, IntPtr.Zero); + if(result != 0) throw new InvalidOperationException("Mode test failed: " + result); + result = ChangeDisplaySettingsEx(name, ref m, IntPtr.Zero, 0, IntPtr.Zero); + if(result != 0) throw new InvalidOperationException("Mode apply failed: " + result); + var actual = Current(name); + if(actual.width != w || actual.height != h || actual.hz != hz || actual.bpp != bpp) + throw new InvalidOperationException("Mode read-back mismatch"); + return; + } + throw new InvalidOperationException("Requested mode not advertised"); + } +} +'@ +$device = [VdiModeProbe]::Find() +if (-not $device) { throw 'No active Virtual Display Driver in this session' } +$current = [VdiModeProbe]::Current($device) +if ($Action -eq 'List') { + [VdiModeProbe]::List($device) | Where-Object { $_.width -eq $Width -and $_.height -eq $Height } | + Select-Object width,height,hz,bpp | ConvertTo-Json -Compress + exit 0 +} +if ($Action -eq 'Apply') { + if (-not (Test-Path -LiteralPath $BackupPath)) { + New-Item -ItemType Directory -Force (Split-Path $BackupPath) | Out-Null + @{device=$device; width=$current.width; height=$current.height; hz=$current.hz; bpp=$current.bpp} | + ConvertTo-Json | Set-Content -LiteralPath $BackupPath + } + try { + [VdiModeProbe]::Apply($device,$Width,$Height,$RefreshRate,$current.bpp) + } catch { + [VdiModeProbe]::Apply($device,$current.width,$current.height,$current.hz,$current.bpp) + throw + } +} +if ($Action -eq 'Restore') { + $before = Get-Content -LiteralPath $BackupPath -Raw | ConvertFrom-Json + if ($before.device -ne $device) { throw 'Display identity changed; inspect before restoring' } + [VdiModeProbe]::Apply($device,$before.width,$before.height,$before.hz,$before.bpp) +} +$current = [VdiModeProbe]::Current($device) +@{device=$device; width=$current.width; height=$current.height; hz=$current.hz; bpp=$current.bpp; action=$Action} | + ConvertTo-Json -Compress diff --git a/tools/stream-motion-test.html b/tools/stream-motion-test.html new file mode 100644 index 0000000..efcfd04 --- /dev/null +++ b/tools/stream-motion-test.html @@ -0,0 +1,39 @@ + + +VDI stream motion test + +
+ diff --git a/tools/test-moonlight-latency.sh b/tools/test-moonlight-latency.sh new file mode 100644 index 0000000..d2ab334 --- /dev/null +++ b/tools/test-moonlight-latency.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# Run against the actual patched source and private runtime on the ARM64 box. +set -euo pipefail +b="${VDI_RKMPP_BUILD_ROOT:-$HOME/vdi-build/rkmpp-experiment}" +sdk="$b/deps/usr" +export LD_LIBRARY_PATH="$b/runtime/lib:$sdk/lib/aarch64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +export PKG_CONFIG_PATH="$b/runtime/lib/pkgconfig:$sdk/lib/aarch64-linux-gnu/pkgconfig:$sdk/share/pkgconfig" +export CPLUS_INCLUDE_PATH="$sdk/include/aarch64-linux-gnu:$sdk/include" +export C_INCLUDE_PATH="$sdk/include/aarch64-linux-gnu:$sdk/include" +sanitize=() +if [ "${VDI_TEST_SANITIZE:-0}" = 1 ]; then + sanitize=(-fsanitize=address -g) +fi +g++ "${sanitize[@]}" -std=c++17 -fPIC -UNDEBUG "$b/latency-tests/test_latency.cpp" \ + -I"$b/src/moonlight/app/streaming/input" \ + -I"$b/src/moonlight/app/streaming/video/ffmpeg-renderers/pacer" \ + -I"$b/runtime/include" $(pkg-config --cflags --libs Qt5Core sdl2) \ + -L"$b/runtime/lib" -lavutil -o "$b/latency-tests/test_latency" +"$b/latency-tests/test_latency" + +g++ "${sanitize[@]}" -std=c++17 -fPIC -UNDEBUG "$b/latency-tests/test_input_priority.cpp" \ + -I"$b/src/moonlight/app/streaming/input" $(pkg-config --cflags --libs sdl2) \ + -o "$b/latency-tests/test_input_priority" +"$b/latency-tests/test_input_priority" +gcc "${sanitize[@]}" -std=gnu11 -ffunction-sections -fdata-sections -Wl,--gc-sections -UNDEBUG \ + -I"$b/src/moonlight/moonlight-common-c/moonlight-common-c/src" \ + -I"$b/src/moonlight/moonlight-common-c/moonlight-common-c/enet/include" \ + "$b/latency-tests/test_absolute_input.c" \ + "$b/build/moonlight/moonlight-common-c/libmoonlight-common-c.a" \ + -lpthread -lcrypto -o "$b/latency-tests/test_absolute_input" +"$b/latency-tests/test_absolute_input" diff --git a/tools/validate-sunshine-nvenc.ps1 b/tools/validate-sunshine-nvenc.ps1 new file mode 100644 index 0000000..22348ec --- /dev/null +++ b/tools/validate-sunshine-nvenc.ps1 @@ -0,0 +1,105 @@ +param( + [string]$SunshineLogPath = "", + [int]$MaxLogAgeMinutes = 30 +) + +# 只读验收脚本:不重启 VM、设备、VFIO 或 Sunshine,不修改任何配置。 +# 退出码 0 才表示当前状态满足“可继续做串流性能验收”的前置条件。 +$ErrorActionPreference = "Stop" +$ok = $true + +function Report([string]$Name, [bool]$Passed, [string]$Detail) { + $state = if ($Passed) { "PASS" } else { "FAIL" } + Write-Output ("[{0}] {1}: {2}" -f $state, $Name, $Detail) +} + +$nvidiaCommand = Get-Command nvidia-smi -ErrorAction SilentlyContinue +if ($null -eq $nvidiaCommand) { + Report "nvidia-smi" $false "命令不存在" + $ok = $false +} else { + $ErrorActionPreference = 'Continue' + $gpuText = & $nvidiaCommand.Source --query-gpu=name,driver_version,pstate --format=csv,noheader 2>&1 + $queryExitCode = $LASTEXITCODE + $ErrorActionPreference = 'Stop' + if ($queryExitCode -ne 0 -or [string]::IsNullOrWhiteSpace(($gpuText -join ""))) { + Report "nvidia-smi" $false (($gpuText -join " ").Trim()) + $ok = $false + } else { + Report "nvidia-smi" $true (($gpuText -join " ").Trim()) + } +} + +try { + $displayAdapters = @(Get-CimInstance Win32_VideoController | + Where-Object { $_.Name -match "NVIDIA|GeForce|Quadro|Tesla" }) + $healthy = @($displayAdapters | Where-Object { $_.ConfigManagerErrorCode -eq 0 }).Count -gt 0 + Report "NVIDIA PnP" $healthy (($displayAdapters | ForEach-Object { "$($_.Name): code=$($_.ConfigManagerErrorCode)" }) -join "; ") + $ok = $ok -and $healthy + if (-not ('VdiDiagnostics.D3DProbe' -as [type])) { + Add-Type -Path (Join-Path $PSScriptRoot 'probe-nvidia-d3d.cs') + } + $dxgi = @([VdiDiagnostics.D3DProbe]::Check()) + $healthy = @($dxgi | Where-Object { $_.DeviceResult -eq 0 }).Count -gt 0 + $detail = if ($dxgi.Count) { ($dxgi | ForEach-Object { '{0}: HRESULT=0x{1:X8}' -f $_.Name,$_.DeviceResult }) -join '; ' } else { 'DXGI did not enumerate an NVIDIA adapter' } + Report "NVIDIA DXGI + D3D11" $healthy $detail + $ok = $ok -and $healthy +} catch { + Report "NVIDIA DXGI + D3D11" $false $_.Exception.Message + $ok = $false +} + +if ([string]::IsNullOrWhiteSpace($SunshineLogPath)) { + $candidates = @( + "C:\Program Files\Sunshine\config\sunshine.log", + "C:\ProgramData\Sunshine\config\sunshine.log" + ) + $SunshineLogPath = $candidates | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1 +} + +if ([string]::IsNullOrWhiteSpace($SunshineLogPath) -or -not (Test-Path -LiteralPath $SunshineLogPath)) { + Report "Sunshine encoder" $false "找不到 sunshine.log;不能证明实际使用 h264_nvenc" + $ok = $false +} else { + # Encoder discovery/probing can mention both NVENC and x264. Inspect only + # the most recent actual creation, with a timestamp, never the entire history. + $pattern = '\[(?