先说结论:要在 Linux 上跑通虚幻引擎(UE)的像素流送(Pixel Streaming),完全可行,而且一旦跑顺了,比 Windows 部署更省心——但它绝不是把 Windows 那套命令照搬过来就能收工的。很多团队在 Windows 开发机上几分钟就能把 Pixel Streaming 的示例页面弹出来,换到 Linux 服务器上就开始各种黑屏、无编码器、信令握手失败。我踩过一轮之后回头看,问题基本不在 UE 本身,而是对 Linux 下的图形栈、GPU 权限、无显示环境、WebRTC 信令链路不熟。这篇文章是我把一个 UE 项目从 Windows 开发机搬到 Linux 服务器做像素流送部署的完整记录,涵盖环境准备、项目打包、信令服务器、启动流程、差异对照和常见坑点,适合有 UE 基础但 Linux 经验不太多的开发者参考。
1. 项目概述与部署思路
1.1 像素流送到底解决什么问题
像素流送的核心思路是把 UE 渲染出来的画面通过 WebRTC 实时推送到浏览器,终端用户不需要下载安装包,不需要高配电脑,打开网页就能操作一个运行在服务器上的实时 3D 应用。典型场景包括数字孪生、建筑方案展示、产品在线配置器、虚拟展厅,还有需要多人同时查看同一画面的协作评审。
这里有个很容易被忽略的点:像素流送不只是“传视频”。它同时承担了交互事件的双向通道,浏览器端的鼠标键盘操作会通过信令通道发回 UE 进程,UE 处理后,画面帧再编码推回来。所以它天然适合“终端零安装 + 内容集中在服务端”的业务。资产和场景数据不出服务器,浏览器看到的只是一路视频流,从内容安全角度也有优势。对最终用户来说,Chrome 打开链接就能用,完全没有 UE 客户端的概念。
1.2 为什么偏偏要部署在 Linux 上
很多团队的开发机是 Windows,但生产服务器往往希望用 Linux。原因很现实:Linux 服务器没有桌面环境,资源占用低;系统长时间运行更稳定;云主机、容器化、自动化运维那一套工具链对 Linux 的支持最好;许可和镜像成本通常也更低。如果一个 UE 应用要作为常驻服务跑在机房或云上,Windows Server 虽然也能做,但多年运维下来,大家更倾向 Linux。
但把 UE 跑在 Linux 上有个天然门槛:虚幻引擎对 Linux 的支持虽然完整,却不像 Windows 那样“开箱即用”。尤其是像素流送,它依赖 GPU 硬件编码、Vulkan 渲染、无头显示环境,任何一个环节不对,浏览器端就是一张黑屏。再加上 UE 官方文档里关于 Linux 部署的细节比较分散,很多参数要靠试错才能确认。这篇文章的定位就是把这条链路从头到尾打通。
1.3 整体部署链路
像素流送的完整链路可以拆成几段:
- UE 应用进程:渲染 3D 场景,接收浏览器回传的交互事件。
- 像素流送插件:采集渲染画面,交给编码器编码为视频流,同时处理 WebRTC 协商。
- 信令服务器:负责浏览器与 UE 进程之间的连接配对,传递 SDP 和 ICE 候选。
- 浏览器端播放器:接收视频流并渲染到网页,同时把用户输入发回 UE。
- 辅助设施:Nginx 反向代理、TURN/STUN 服务、HTTPS 证书,按部署环境按需引入。
整个部署过程中,最容易被忽略的是“信令先于媒体”这个时序。浏览器要先跟信令服务器握上手,再由信令服务器牵线找到 UE 进程,之后两者才能建立 WebRTC 连接。很多人启动 UE 后浏览器一直转圈不弹画面,十有八九是信令服务器没起、端口不通,或者 UE 进程没有连上信令服务器。后面我会按启动顺序一步步讲。
2. 服务器环境准备
2.1 系统选型与基础配置
我这次用的是 Ubuntu Server 22.04 LTS,选它的原因主要是 UE 官方对 Ubuntu 的兼容性说明最明确,社区踩坑案例也多。内核和图形栈的更新节奏适中,不像某些滚动发行版那样动不动把驱动搞崩。系统安装时建议选最小化安装,不需要装桌面环境,也不需要装多余的服务。装机后第一件事是更新软件源和系统包:
sudo apt update && sudo apt upgrade -y然后确认硬件信息。像素流送对 CPU 和内存的要求取决于场景复杂度,如果是几千平方米的 BIM 模型加实时光照,建议 8 核以上 CPU、32GB 内存起步;如果只是简单产品展示,4 核 16GB 也能跑,但编码和渲染都吃资源,宁可多配不要少配。显卡方面,NVIDIA 显卡是首选,因为像素流送的硬件编码走 NVENC,AMD 和 Intel 在 Linux 下的编码器支持比较折腾,不建议拿来生产。
lspci | grep -i nvidia nvidia-smi先看系统能不能认出显卡。如果nvidia-smi不存在,说明驱动还没装;如果 lspci 里根本看不到显卡,那要检查是不是云主机没分配 GPU 直通,或者物理机显卡没插好。这一步排查清楚再往下走,不然后面所有问题都会归因到“显卡不存在”上。
2.2 GPU 驱动与视频编码环境
Linux 下装 NVIDIA 驱动有几种方式,我推荐用官方驱动加 CUDA 库的组合。先禁用系统自带的 nouveau 开源驱动,不然会和官方驱动冲突:
sudo bash -c "echo 'blacklist nouveau' >> /etc/modprobe.d/blacklist-nouveau.conf" sudo bash -c "echo 'options nouveau modeset=0' >> /etc/modprobe.d/blacklist-nouveau.conf" sudo update-initramfs -u然后重启机器,确认 nouveau 已经不再加载:
lsmod | grep nouveau没有输出就说明屏蔽成功。下载 NVIDIA 驱动时要注意选择对应显卡型号和系统架构的版本,建议直接去官方驱动页查型号下载。安装过程如果用 runfile 方式,要确保编译内核模块所需的 gcc、make、kernel headers 已经装好:
sudo apt install build-essential linux-headers-$(uname -r) -y sudo sh ./NVIDIA-Linux-x86_64-xxx.xx.run安装完成后用nvidia-smi验证,能看到 GPU 型号、驱动版本、显存容量就基本没问题。像素流送编码要走 NVENC,驱动程序版本不能太老,建议使用较新的稳定版;太老的驱动可能不支持新版 UE 调用的编码接口。
这一步有个容易忽略的细节:检查/dev/nvidia0和/dev/nvidiactl是否存在。如果设备节点没生成,UE 进程虽然能跑,但硬编码的时候会报错。另外有些云主机会把显卡设备权限限制住,如果 UE 用普通用户启动,需要确认用户对/dev/nvidia*有读写权限。
2.3 无显示环境与图形栈处理
Linux 服务器通常没有显示器,UE 要渲染画面就得处理“往哪画”的问题。UE 像素流送提供了离屏渲染模式,通过启动参数-RenderOffScreen让引擎不创建窗口,画面直接渲染到后台缓冲区再交给编码器。这样不需要 X Server,省掉不少麻烦。
但离屏渲染不等于不需要图形库。UE 在 Linux 下走 Vulkan 渲染,所以要安装 Vulkan 运行时和依赖库。另外还需要一些运行时链接库,缺了它们 UE 进程会启动失败。我用到的依赖安装命令大致如下:
sudo apt install libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libasound2-dev libpulse-dev libgl1-mesa-dev mesa-common-dev libvulkan1 libvulkan-dev vulkan-tools如果机器上有声卡设备但不想让 UE 抢音频,或者干脆没有音频设备,可以装一个虚拟声卡模块让 UE 的音频采集不报错:
sudo apt install snd-aloop sudo modprobe snd-aloop这个不是必须的,但服务器如果不装声卡,UE 启动时可能会刷一条音频设备初始化的警告,某些版本还会因为找不到音频设备而崩溃。我建议在服务器环境里把虚拟声卡装上,对音频采集类需求也有帮助。
所有依赖装完后,可以跑一下vulkaninfo --summary确认 Vulkan 能识别到渲染设备和编码设备。如果这里输出为空或者只看到软件渲染器,那说明驱动装得有问题,后面 UE 肯定也起不来。
3. UE 项目打包与像素流送插件配置
3.1 插件启用与参数
在 UE Editor 里打开项目,确认 Pixel Streaming 插件已经启用。这个插件在 UE4.26 之后属于官方标准插件,一般不需要额外下载,位置在“编辑-插件”窗口的“流送”分类下。启用后重新启动编辑器,插件会生成一些默认配置。
像素流送有几个关键参数要在项目配置里确认:
-PixelStreamingIP:信令服务器的 IP 或域名,UE 进程启动时用这个参数告诉插件去连接哪个信令服务器。-PixelStreamingPort:信令服务器的 WebSocket 端口,默认通常是 80 或自定义端口。-RenderOffScreen:离屏渲染开关,Linux 无显示环境必备。-PixelStreamingCodec:编码格式,常见是 H.264,新版本也可能支持 AV1,需要根据浏览器兼容性选择。
这些参数可以直接写在命令行里,也可以写进DefaultEngine.ini。我建议命令行传参为主,因为部署环境变了只要改启动脚本就行,不用重新打包。插件在项目打包时会被一起编进去,只要插件启用且没在打包设置里被排除。
3.2 Linux 打包关键设置
如果你在 Windows 开发机上做事,打包 Linux 版需要先在启动器里安装 Linux 平台的工具链。UE 编辑器默认只带本机平台,打包其他平台会提示缺少组件。安装好之后,文件菜单里的“打包项目”会出现 Linux 选项。Linux 服务器版本的项目类型可以选“Linux Server”,但我实际测下来,像素流送用“Linux”类型的包更稳妥,因为 Server 类型会裁剪掉一些渲染和媒体相关的模块。
如果你直接在 Linux 机器上装 UE Editor 来打包,那就不存在交叉编译的问题,但要注意编辑器本身也要能跑起来,显卡驱动和图形库必须先装好。打包命令如果走自动化管线,可以用 UnrealBuildTool 配合 RunUAT 脚本:
./Engine/Build/BatchFiles/RunUAT.sh BuildCookRun \ -project=/path/to/YourProject.uproject \ -platform=Linux \ -clientconfig=Development \ -build -cook -stage -pak -archive \ -archivedirectory=/path/to/output打包完成后,输出目录里能找到一个可执行文件,名字和项目名一致。这个可执行文件加上-RenderOffScreen和像素流送参数,就是最终的运行程序。
3.3 打包后的目录结构
Linux 打包产物跟 Windows 的结构类似,但要注意几个细节:
- 可执行文件需要添加执行权限:
chmod +x YourProject。 - 某些 .so 动态库依赖系统的 libc、libstdc++ 版本,一般 Ubuntu 22.04 没问题,但如果你跑在更旧的发行版上,可能会遇到 GLIBC 版本不兼容,那就只能换系统或升级。
- 可以用
ldd ./YourProject | grep "not found"检查缺失的依赖库。我之前就碰到过缺libnvidia-glcore.so的情况,原因是 32 位兼容库没装,后来在 Ubuntu 上装了libnvidia-gl-xxx才解决。
还有一点:打包目录里有一个YourProject/Saved/文件夹,运行时日志写在里面。排查问题第一件事就是看Logs/YourProject.log,很多问题不用猜,日志里写得很明白。
4. 信令服务器与流媒体链路部署
4.1 官方信令服务器部署
信令服务器的作用是让浏览器和 UE 进程先建立“联系”。UE 早期的像素流送方案需要单独从官方仓库拉取 PixelStreamingInfra,部署方式是基于 Node.js 的服务。新版本 UE 已经把信令服务器集成到了插件或示例目录里,不同版本位置不一样。我这次用的是 UE5.3,在引擎安装目录下的 Samples/PixelStreaming 里能找到现成的信令服务器程序,相比早期版本省了很多配置工作。
如果是自己拉取 PixelStreamingInfra 部署,流程是:
git clone https://github.com/EpicGames/PixelStreamingInfra.git cd PixelStreamingInfra/SignallingServer npm install npm start启动后信令服务器默认监听 80 端口。你可以先用浏览器访问http://服务器IP,如果能看到信令服务器默认页面或一个简单的连接测试页面,就说明服务正常。Node.js 版本建议 16 以上,太老跑不起来新版依赖。
这里有个容易被忽略的点:信令服务器的 WebSocket 端口和 UE 进程的媒体端口不是一回事。浏览器先通过 WebSocket 连信令服务器,信令服务器再把 UE 进程的连接信息返回给浏览器,之后媒体数据走的是另一条 WebRTC 通道。所以“信令服务器能访问”不代表“像素流送能通”,后面要分开排查。
4.2 配置 TURN、STUN 与反向代理
内网部署时,浏览器和服务器在同一内网,一般不需要 TURN/STUN。但如果服务器在云上,或者浏览器和服务器之间有复杂的 NAT 和防火墙,WebRTC 的 P2P 连接可能建立不起来,这时候就需要 STUN/TURN 服务器辅助。STUN 负责探测公网地址,TURN 则在 P2P 失败时中转流量。
最常用的开源方案是 coturn:
sudo apt install coturn配置 TURN 服务时,需要指定监听端口和认证方式。生产环境建议启用静态认证,并把 TURN 端口在防火墙里放开。如果只是测试,临时跑一下也可以。
如果还要走 HTTPS 和域名访问,Nginx 可以作为反向代理放在信令服务器前面。WebRTC 在浏览器里有一些安全上下文限制,生产环境强烈建议启用 HTTPS,否则部分浏览器会拒绝 WebRTC 连接或静音视频流。Nginx 配置需要额外处理 WebSocket 的升级头:
location / { proxy_pass http://127.0.0.1:80; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }这只是信令层面的反代,真正的媒体流端口还需要在安全组或防火墙里放行。具体端口因 UE 版本而异,通常 UE 进程会监听一个媒体端口用于 WebRTC 传输,需要提前规划好并放行。
4.3 用 systemd 管理服务
像素流送的两个核心进程——信令服务器和 UE 应用——都应该用 systemd 托管,避免 SSH 断开后进程被一起带走。为信令服务器写一个 service 文件:
[Unit] Description=UE Pixel Streaming Signalling Server After=network.target [Service] WorkingDirectory=/opt/pixelstreaming/SignallingServer ExecStart=/usr/bin/node /opt/pixelstreaming/SignallingServer/SignallingServer.js Restart=always RestartSec=5 User=pixelstream Environment=NODE_ENV=production [Install] WantedBy=multi-user.targetUE 应用进程的 service 文件类似,但要注意启动参数和环境变量。如果 UE 崩溃,systemd 的Restart=always会自动拉起来,对生产环境很有用。日志查看也比较方便:journalctl -u pixelstreaming-ue -f。
我习惯把服务名区分开,信令叫signalling,UE 叫ue-streamer,这样看状态和日志不会混淆。
5. 实际启动流程与联调
5.1 启动顺序与参数
启动顺序很重要,信令服务器要先起,UE 进程再连上来。顺序反了虽然 UE 进程会重试,但第一次握手会多出很多等待,看起来就像卡死了一样。
示范命令:
# 第一步,启动信令服务器 cd /opt/pixelstreaming/SignallingServer npm start # 第二步,启动 UE 应用 cd /opt/ue/YourProject ./YourProject \ -RenderOffScreen \ -PixelStreamingIP=127.0.0.1 \ -PixelStreamingPort=80 \ -PixelStreamingCodec=h264 \ -windowed \ -ResX=1280 \ -ResY=720我用127.0.0.1做信令地址是因为信令服务器和 UE 进程在同一台机器上。如果分开部署,要改成信令服务器的实际地址。分辨率参数控制渲染画幅,码率和延迟控制可以在下游的 WebRTC 参数里调,也可以在 UE 启动时加一些像素流送特有参数。
启动后观察 UE 日志,如果一切正常,会出现类似“WebRTC video encoder initialized”或“Signalling server connected”的关键字。看到这些再打开浏览器,基本就能出画面了。
5.2 浏览器端连接验证
浏览器打开信令服务器提供的页面,通常是http://服务器IP。按下 F12 打开开发者工具,在控制台和网络面板里观察 WebSocket 连接状态。正常情况下会看到:
- WebSocket 到信令服务器的连接建立成功。
- 浏览器发出加入或查询请求。
- 信令服务器返回 UE 进程的连接信息。
- ICE 候选交换完成后,媒体通道建立,视频画面出现。
如果停留在某个阶段不动,就顺着对应环节查。WebSocket 连不上查信令端口,SDP 交换不出来查 UE 进程是否活着,媒体流黑屏查编码器和显卡。
我建议在测试阶段用同一台电脑开两个浏览器标签页,一个看画面,一个开开发者工具抓连接过程。很多问题看日志比看黑屏更容易定位。新会话连接时,如果 UE 进程已经在运行,浏览器应该能在几秒内看到画面;如果等待时间过长,检查是不是信令服务器同时只能支持一个 UE 实例,或者端口被占用了。
5.3 Windows 与 Linux 部署差异对照
把像素流送从 Windows 切换到 Linux,最大的感受是思路要变。下面这个对照表是我实践后的总结,比较有代表性:
| 对比项 | Windows 部署 | Linux 部署 |
|---|---|---|
| 渲染 API | D3D11/D3D12 | 主要是 Vulkan |
| 窗口模式 | 可开窗口,也可离屏 | 无桌面环境,必须离屏 |
| 显示驱动 | 显卡厂商驱动自动管理 | 手动装驱动,注意屏蔽 nouveau |
| 编码器 | NVIDIA NVENC,驱动装好即可 | NVENC 依赖驱动和 Vulkan 栈 |
| 启动方式 | 双击或批处理 | systemd 或 shell 脚本 |
| 依赖库 | DLL 大多随包携带 | 依赖系统 .so,需手动检查缺失 |
| 日志排查 | 事件查看器加 UE 日志 | journalctl 加 UE 日志 |
| 远程维护 | 远程桌面 | SSH,注意进程会话管理 |
Windows 部署时,桌面环境会把很多事情“顺便”解决掉,比如显示设备、音频设备、显卡驱动初始化。Linux 部署没有这些隐形帮助,每一个环节都要自己确认。但也正因为如此,Linux 部署一旦跑通,运行环境非常干净,资源占用低,崩溃重启也容易自动化。
6. 常见问题排查与避坑记录
6.1 黑屏无画面
黑屏是像素流送 Linux 部署里最常遇到的问题,而且原因有好几种。我建议按照下面顺序排查:
- 看 UE 日志是否正常启动,是否出现渲染错误。
- 确认 UE 进程有没有崩溃,
ps aux | grep YourProject看看进程还在不在。 - 执行
nvidia-smi,确认 GPU 上有 UE 进程在占用显存。 - 看信令服务器端日志,确认有没有收到 UE 的注册。
- 浏览器开发者工具里看 WebRTC 的
iceConnectionState,如果不是 connected,说明媒体通道没建立。
我踩过的一个典型坑是:UE 进程起来了,信令也通了,但画面一直是黑屏,日志里也没有明显错误。最后发现是显卡驱动没问题,但缺少 Vulkan 的 ICD 配置文件,导致 UE 用软件渲染跑了一圈,编码接口拿不到 NVENC。解决办法是重装 vulkan 驱动,并确认vulkaninfo --summary里的 deviceName 是 NVIDIA 显卡而不是 llvmpipe。
6.2 视频编码失败
启动日志如果出现Encoder not available、Failed to create video encoder、NVENC error这类关键字,基本可以锁定是编码器的问题。一种是驱动太老,另一种是 GPU 型号太老不支持 NVENC,还有一种是权限问题,进程无法访问 /dev/nvidia*。
排查命令:
journalctl -u ue-streamer -n 50 dmesg | grep -i nvidia如果确认是权限问题,最简单的做法是把运行 UE 的用户加入video组:
sudo usermod -aG video pixelstream还有一种情况是并发编码数超过显卡限制。消费级显卡对 NVENC 会话数有上限,多开几个 UE 实例后,后面的实例就会抢不到编码器。如果是这种情况,要么换专业卡,要么降低并发实例数。
6.3 延迟过高
像素流送本身会有一定延迟,但延迟高到影响操作就需要调优。影响延迟的因素包括:编码码率、分辨率、帧率、网络 RTT、WebRTC 缓冲策略。我一般先看网络,局域网里如果延迟还在 200ms 以上,问题多半在编码参数或缓冲设置。
常见调整手段:
- 提高帧率,减少丢帧导致的画面卡顿。
- 调整码率,不要设得过低,否则编码器会为了压缩而引入延迟。
- 在 UE 启动参数里关掉垂直同步,减少渲染管线等待。
- 检查浏览器端的硬件加速是否开启,软件解码也会增加延迟。
Linux 下特别要注意的是离屏渲染模式下,如果服务器 CPU 或 GPU 负载过高,编码器排队会非常明显。用nvidia-smi看 GPU 利用率和编码器利用率,如果编码器一直在满负荷,说明并发已经到瓶颈了。
6.4 端口与信令问题
浏览器访问信令服务器没问题,但 WebSocket 连接失败,或者浏览器能连信令但 UE 进程连不上信令,这是端口和地址配置的问题。先确认信令服务器监听的端口,再确认防火墙规则:
sudo ufw status sudo netstat -tlnp | grep -E '80|8888'如果服务器有多个网卡,注意 UE 启动参数里-PixelStreamingIP应该填浏览器能访问到的那个地址,不能填localhost。浏览器到信令服务器、信令服务器到 UE 进程、UE 进程到信令服务器这三条链路的地址要能互通,缺一条都不行。
有的发行版默认开了 SELinux,会拦截非标准端口的 WebSocket 流量。如果是 CentOS 或 Rocky Linux,检查 SELinux 状态,必要时候把对应端口加入允许列表,或者临时 setenforce 0 测试。
6.5 权限与崩溃问题
用 root 用户跑 UE 进程不是一个好习惯。UE 在 Linux 下以 root 身份运行时,某些模块会尝试读取用户配置文件,权限不一致会导致奇怪的问题。建议创建一个专用用户,比如pixelstream,把打包目录和日志目录的所有权交给它:
sudo useradd -m pixelstream sudo chown -R pixelstream:pixelstream /opt/ue崩溃自动重启用 systemd 配置。除了Restart=always,我还习惯加一个健康检查,用一个 cron 脚本定时探测信令服务器的 HTTP 接口,发现异常就重启服务。这样做的好处是,即使 systemd 没有检测到进程退出,比如某个线程死锁,也能通过外部探测恢复。
内存不足也是 UE 进程崩溃的常见原因。UE 打包后默认可能吃掉好几个 GB 内存,如果服务器内存不够,OOM Killer 会直接杀掉 UE 进程。用journalctl -k | grep -i oom能查到相关记录,这个问题加 swap 只是治标,最好的方案是加内存或者降低场景复杂度。
7. 一点生产环境建议
像素流送服务跑起来之后,下一步要考虑的是可维护性。我这里只说几个自己实践下来觉得划算的投入。
第一个是日志集中处理。UE 进程的日志和信令服务器的日志分开存放,再配一个简单的 logrotate 做轮转,避免磁盘被日志填满。第二个是监控 GPU 状态,nvidia-smi可以定期记录显存、温度、编码器利用率,既能发现硬件故障的苗头,也能判断当前负载离瓶颈还有多远。第三个是给 UE 进程加一个看得见的版本号,在启动脚本里把 Git 提交号打到日志里,不然多版本迭代时根本不知道服务器上跑的是哪一版。
我个人的体会是,Linux 部署像素流送真正难的不是 UE,而是把 Linux 图形栈、GPU 驱动、WebRTC 信令这三条线串起来。每一条线单独看都有完整文档,但放到一起就全是文档没写的细节。如果你也准备做这件事,先从最小场景开始:用示例项目在本地 Linux 机器上跑通,再逐步加真实场景和并发,不要一上来就把所有环节都堆到生产环境里。这样每一步出问题,都知道该去查哪个方向。