基于 xnasdesktop、pcweb、player-qt、hdmi-h5 等组件的逆向分析 分析日期:2026-05-22
说明:本篇写于 Web 桌面技术栈确认之前,文中"React + Vue.js 混合"的判断已被第 6 篇修正——pcweb 实际是纯 Vue 3 应用。本篇的价值在于 HDMI 桌面(Qt 壳)、原生播放器和 Web-Native 通信机制的分析,这些结论不受影响。
一、核心结论:Qt 壳 + Web 内容 + 原生播放器的混合架构
极空间的"桌面系统"有两套实现,但共享同一套 Web 前端代码:
┌─────────────────────────────────────────────────────┐
│ 桌面系统入口 │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ 浏览器访问 │ │ HDMI 输出(接电视/显示器)│ │
│ │ :80 / :8080 │ │ xnasdesktop 原生应用 │ │
│ └────────┬─────────┘ └───────────┬──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ pcweb (SPA) │ │ xnasdesktop (Qt5) │ │
│ │ nginx 静态文件 │ │ + Chromium WebView │ │
│ │ │ │ + 原生 Qt 播放器 │ │
│ └────────┬─────────┘ └───────────┬──────────┘ │
│ │ │ │
│ └──────────┬───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ 同一套 Web 前端代码 │ │
│ │ Vue 3 + Vue Router + │ │
│ │ ECharts │ │
│ └───────────────────────┘ │
└─────────────────────────────────────────────────────┘
二、xnasdesktop:原生 Qt 桌面壳
是什么
xnasdesktop 是一个 Qt5 C++ 桌面应用,安装在 /zspace/applications/services/xnasdesktop/。它不是 Electron,而是直接用 Qt5 写的原生应用 + 系统 Chromium 做 WebView。
关键文件
| 文件 | 大小 | 作用 |
|---|---|---|
xnasdesktop | 3.3MB | 桌面壳主程序 |
xnasdesktop-daemon | 5.0MB | 后台守护进程 |
pdfview | 141KB | 原生 PDF 阅读器 |
libQt5*.so | 约 50MB | 自带的 Qt5 运行时库 |
打包方式
使用 linuxdeployqt 工具打包,所有 Qt 依赖自包含:
qt.conf:
[Paths]
Prefix = ./
Plugins = plugins
Imports = qml
不依赖系统 Qt,升级时可整体替换。
启动流程
# realstart.sh — 桌面壳启动逻辑
export DISPLAY=:0.0
export QTWEBENGINE_DISABLE_SANDBOX=1 # Qt WebEngine 以 root 运行
export GST_MPP_VIDEODEC_DEFAULT_ARM_AFBC=1 # Rockchip 硬解
killall chromium-bin # 清理残留
./xnasdesktop-daemon & # 启动守护进程
关键点:xnasdesktop-daemon 是实际启动的进程,它负责:
- 创建桌面窗口
- 管理壁纸
- 启动 Chromium WebView
- 处理 HDMI 热插拔
- 管理原生播放器生命周期
桌面配置
# conf.ini
[Player]
Path=/zspace/applications/services/xnasplayer/ # 原生播放器路径
[Xnas]
UseChrome=1 # 使用 Chromium 渲染 Web
Url="http://127.0.0.1:20123/home?locale=zh_cn" # Web UI 地址
Wallpaper=/zspace/system/resource/T2_default.jpg # 壁纸路径
ChromeStartType=0 # Chromium 启动模式
Locale=zh_cn # 语言
LockBaseTime=60000 # 锁屏时间
当前设备状态
// daemon.json
{"enable_desktop": false}
被分析 T2 的 HDMI 桌面是关闭状态(T2 是便携 NAS,通常不接显示器)。
机型判断逻辑:
# T022 型号禁用桌面(某些低端型号无 HDMI 输出)
if grep -qE "T022" "$ID_FILE"; then
echo "xnasdesktop disabled on zspace t022"
exit 0
fi
三、pcweb:Web 桌面 SPA
是什么
pcweb 是一个单页应用(Vue 3,见第 6 篇),由 nginx 提供静态文件服务。这是用户在浏览器中看到的桌面界面。
打包产物
| 库 | 文件 | 用途 |
|---|---|---|
| Vue 3 | lib-vue.dc72da37.js | 核心框架 |
| Vue Router | lib-router.22ab4569.js | 前端路由 |
| Axios | lib-axios.b14a43f9.js | HTTP 请求 |
| ECharts | echarts.ab94bd45.js | 图表(系统监控等) |
多入口架构
pcweb 为不同场景提供不同的入口:
/zspace/applications/services/pcweb/
├── home/
│ ├── index.html ← PC 浏览器入口
│ ├── index-abroad.html ← 海外版入口
│ ├── index-local.html ← 局域网入口
│ ├── mobile/ ← 手机浏览器入口
│ └── static/js/ ← 打包后的 JS/CSS
│ ├── index.c3f6052c.js 主包
│ ├── lib-vue.dc72da37.js Vue 运行时
│ ├── lib-router.22ab4569.js Vue Router
│ ├── lib-axios.b14a43f9.js HTTP 客户端
│ └── echarts.ab94bd45.js ECharts 图表
│
└── AppH5/
├── index.html ← App 内嵌 H5 入口
└── static/ ← 独立的打包产物
应用路由:nginx Lua 动态分发
pcweb 最精巧的设计是应用路由。访问 /home/video 时,nginx 用 Lua 动态路由到对应应用的前端:
location /home/ {
access_by_lua_block {
local path_mapping = {
album = "photoAlbum", # /home/album → photoAlbum 应用
video = "video", # /home/video → video 应用
music = "music", # /home/music → music 应用
download = "downloads", # /home/download → downloads 应用
ddns = "ddns",
onlyoffice = "onlyoffice",
camera = "camera",
backupCenter = "backupCenter",
deepSeek = "deepseek",
converter = "imgProcessing",
classroom = "zclasses",
-- ... 等约 25 个应用映射
}
-- 动态构造目标路径
-- /home/video → /var/appstore/pkg/video/ui/apphome_video/index.html
-- 如果目标文件存在 → 302 重定向
-- 不存在 → 回退到 pcweb 自身的 SPA 路由
}
}
这意味着:
- 主框架(桌面壳)是 SPA
- 每个子应用(相册、视频、音乐等)是独立的 Web 应用,存放在各自的应用包中
- nginx 作为微前端路由器,动态分发请求
子应用的 Web UI 位置
/var/appstore/pkg/video/ui/apphome_video/index.html
/var/appstore/pkg/photoAlbum/ui/apphome_photoAlbum/index.html
/var/appstore/pkg/music/ui/apphome_music/index.html
/var/appstore/pkg/downloads/ui/apphome_downloads/index.html
...
每个子应用独立打包,可以单独更新。
四、HDMI 桌面:Qt 壳 + Chrome WebView 的混合
架构
xnasdesktop 启动后:
- 渲染壁纸(
T2_default.jpg) - 启动 Chromium 浏览器进程
- Chromium 全屏打开
http://127.0.0.1:20123/home - 20123 端口是专门的 HDMI nginx 入口,代理到 hdmi-h5 前端
xnasdesktop (Qt5 壳)
│
├── 壁纸层 (Qt QPainter/QML)
├── 系统托盘 / 状态栏 (Qt Widgets)
│
└── Chromium (全屏 WebView)
│
└── http://127.0.0.1:20123/home
│
└── nginx → /zspace/applications/services/hdmi-h5/home/
│
└── 同一套 Web 前端代码 (hdmi-h5 版本)
为什么不直接用 Electron?
Electron 在 ARM 上体积太大(200MB+),且自带 Chromium 无法复用系统的硬件解码。极空间的做法更轻量:
- Qt5 壳只有 ~3MB
- Chromium 使用系统安装的版本
- 视频播放切到原生 Qt 播放器(硬件解码)
hdmi-h5:HDMI 版本的 Web 前端
/zspace/applications/services/hdmi-h5/home/
├── index.html ← HDMI 桌面入口
├── qwebchannel.js ← Qt WebChannel 通信桥
└── static/
├── css/
├── js/
└── version
qwebchannel.js 是关键——这是 Qt WebChannel 的 JavaScript 库,用于 Web 页面和 Qt 原生代码之间的双向通信。这意味着 Web UI 可以调用原生 Qt 功能(比如打开原生播放器、读取系统状态)。
两个 HDMI nginx 端口
| 端口 | 用途 | 前端源 |
|---|---|---|
| 20123 | HDMI 桌面 WebView 访问 | hdmi-h5 |
| 8088 | 外网访问 HDMI 桌面(远程控制) | hdmi-h5 |
两个端口配置几乎相同,只是 20123 只监听 localhost(只给本地 Chromium 用),8088 可以被远程访问(通过 5056 代理)。
五、原生播放器:为什么需要 Qt 写播放器
player-qt(主播放器)
/zspace/applications/services/player-qt/
├── player-qt ← 8.8MB Qt5 播放器
├── player-daemon ← 7.8MB 播放守护进程
├── libQt5*.so ← 自包含 Qt5 库
├── libavcodec.so.58 ← FFmpeg 编解码
├── libavformat.so.58
└── libavutil.so.56
配置显示使用了 Rockchip 平台的硬件能力:
[plane-id]
AFBC=1 # ARM FrameBuffer Compression(Rockchip GPU 特性)
video=71 # 视频图层 ID
info=116 # UI 图层 ID
[play-info]
hdr=1 # HDR 支持
xnasplayer(轻量播放器)
[plane]
AFBC=1
video=68 # 视频图层
ui=102 # UI 图层
[player]
hdr=1
kms=0 # 使用 DRM 还是 KMS
为什么不用 Web 播放器?
ARM 设备上用 <video> 标签播 4K HDR 视频体验很差。原生 Qt 播放器直接调用:
- Rockchip MPP(Media Process Platform)硬件解码
- DRM/KMS 零拷贝渲染
- AFBC(ARM FrameBuffer Compression)减少带宽
同样的视频,Web 播放可能 CPU 100% 还卡顿,原生 Qt 播放器 CPU 5% 流畅播放。
六、Web 前端与原生 Qt 的通信机制
qwebchannel.js 暴露了通信桥。典型交互:
Web UI (SPA) Qt 壳 (xnasdesktop)
│ │
├── 用户点击播放视频 │
│ qwebchannel.send({ │
│ action: "play", │
│ file: "/data/video.mp4" │
│ }) │
│─────────────────────────────────►│
│ ├── 启动 xnasplayer
│ │ exec("./xnasplayer video.mp4")
│ │
│ qwebchannel.onMessage({ │
│ event: "playback_started" │
│ }) │
│◄─────────────────────────────────│
这种模式让 Web 技术栈负责 UI 交互,原生 Qt 负责性能敏感的媒体处理。
七、桌面系统的完整布局
用户看到的桌面
│
├── 浏览器访问 (任意设备)
│ │
│ └── nginx :80 / :8080
│ │
│ ├── / → pcweb/home/index.html (SPA 主框架)
│ ├── /home/video → /var/appstore/pkg/video/ui/ (子应用 Web UI)
│ ├── /home/album → /var/appstore/pkg/photoAlbum/ui/
│ ├── /home/music → /var/appstore/pkg/music/ui/
│ └── ...
│
└── HDMI 输出 (接电视/显示器)
│
└── xnasdesktop (Qt5 桌面壳)
│
├── 桌面壁纸 (QPainter 渲染)
├── 状态栏 / 系统托盘 (Qt Widgets)
├── Chromium WebView (全屏)
│ │
│ └── http://127.0.0.1:20123/home
│ │
│ └── hdmi-h5 前端 (与 pcweb 共享代码)
│
├── 原生视频播放器 (player-qt / xnasplayer)
│ 直接调用 GPU 硬解码 (Rockchip MPP + DRM)
│
└── 原生 PDF 阅读器 (pdfview)
八、技术选型分析
极空间的桌面方案有几个聪明的选择:
1. Qt5 而非 Electron
- 体积小(3MB vs 200MB)
- 可以直接调用 DRM/KMS 做 GPU 渲染
- Qt WebChannel 提供了原生-JS 通信
linuxdeployqt工具让打包简单
2. 不强制统一框架
- 各子应用团队可以选择自己熟悉的工具
- nginx Lua 做微前端路由是个巧妙的轻量方案
3. Web 优先,原生辅助
- 95% 的 UI 是 Web
- 只有视频播放和 PDF 用原生
- 新增功能只需改 Web 前端,不需要发系统更新
4. hdmi-h5 和 pcweb 代码共享
- 两个入口加载的是同一套前端代码(不同构建配置)
- 维护成本降低一半
九、如果要自己实现类似方案
推荐的路径:
Phase 1(Web only)
- 前端 SPA(Vue/React 均可)+ 前端路由
- 所有功能都是 Web,视频用
<video>+ MSE - nginx 静态文件服务 + API 代理
- 不支持 HDMI,只支持浏览器
Phase 2(Hybrid — HDMI 输出)
- 用 Tauri 替代 Electron/Qt
- Tauri 在 ARM 上比 Electron 轻 20 倍
- 后端用 Rust 做系统调用
- 支持 WebView 和原生窗口
Phase 3(性能优化)
- 视频播放器用 Rust + gstreamer 或直接绑 FFmpeg
- GPU 渲染通过 Tauri 的 Rust 后端调用 DRM
如果必须用 Qt(比如需要深度的硬件集成),可以参考 xnasdesktop 的做法:
- 自包含 Qt 库(linuxdeployqt 打包)
- Qt WebEngine 做 WebView
- Qt WebChannel 做 JS-Native 桥
- 原生播放器单独进程,通过 IPC 与桌面壳通信