ue4云渲染——像素流送实践

一、安装 Epic Games Launcher

官方中文文档 https://docs.unrealengine.com/4.27/zh-CN/,点击下载

如需要登录账号,使用注册一个账号后再登录就可以了

二、配置库,安装虚拟引擎

启动Epic Games Launcher,安装图解如下:

        安装的过程可能会因为网速比较慢,对硬件的性能有很大的要求,安装的虚拟引擎会有快捷方式,也可以从快捷方式中启动,这样不用每次开Epic Games Launcher,启动之后呈现如下所示,这时候可以按照自己的喜好来选择对应的类型进行项目的尝试

三、像素流送实践

        像素流送是ue4云渲染环境搭建的必学的内容。官方文档 https://docs.unrealengine.com/4.27/zh-CN/SharingAndReleasing/PixelStreaming/PixelStreamingIntro/        有详细的步骤,但是按照步骤还是会遇到一些问题。见此文章中的  四、遇到的问题  放点耐心尝试,看你有没有遇上这些问题呢?

        基于ue4的云渲染目的在与将ue4实时渲染图像帧通过视频流的方式一帧一帧的传到浏览器端,主要是因为基于web的三维渲染效果不佳,通过ue4的强大渲染能力与各种功能的支持能力使得网页端也可以实时看到好的效果。这个过程服务之间使用的是反向代理的机制,过程大概分为三个应用: 前端------(控制命令与视频流)node信令服务--------ue4视频流服务

    1、前端:web端需要通过html+js的方式显示视频与后端交互,需要熟悉前端的一些知识;

     2、信令服务:基于nodejs开发的服务,接受前端发送过来的命令,并将命令转发到ue4服务端,同时接受ue4服务端发送过来的视频流与其他信息转发给前端;

信令服务的目录是 "D:\WindowsNoEditor\Engine\Source\Programs\PixelStreaming\WebServers"

在"WebServers\SignallingWebServer"文件夹下,包含一些用于启动node的批处理文件。例如run.bat文件,其中cirrus.js文件会读取"config.json"中的配置信息启动node服务,这些配置信息比较重要的是一些ip地址和端口,"config.json"

{
    "UseFrontend": false,
    "UseMatchmaker": false,
    "UseHTTPS": false,//是否使用https
    "UseAuthentication": false,//认证信息
    "LogToFile": true,
    "HomepageFile": "player.htm",//访问的主页
    "AdditionalRoutes": {}
}

    3、ue4服务端:接收信令服务的控制命令信息,根据命令调用相应的功能进行渲染,将渲染结果发送给信令服务。

(一)、先决条件

  • 检查操作系统和硬件-

          像素流送插件只能在运行Windows操作系统的电脑上编码视频,对电脑的GPU硬件还有一定要求。欲知详情,请查阅像素流送参考

  • 安装node.js -

          如果电脑上尚未安装 node.js,则需要下载并安装

  • 打开网络端口-

          必须打开本地网络上的以下端口进行通信:80、888。如需修改默认端口,请查阅像素流送参考

  • 停止其他web服务器 -

         如果电脑正在运行其他Web服务器,则将其停止。

  • IP地址-

          需要知悉电脑的IP地址。

        在一个LAN或VPN中开始像素流送为佳,这意味着需要电脑的 内部 IP地址。在命令行或控制台窗口中运行 ipconfig 命令,找到开头为 IPv4 Address 的命令行即可获知此地址。

        如果尝试从不同网络的电脑或移动设备上进行连接,则你可能需要将信令服务器配置成使用STUN/TURN服务器。访问此像素流参考页面了解操作方法。

(二)、准备虚幻引擎应用程序

1、在虚幻编辑器中打开项目。

        tips:官方文档上的:“此页上的步骤图像展示流程中使用的项目 第三人称蓝图 模板构建。实际上该步骤可用于所有虚幻引擎项目。”

        实际操作步骤:启动虚拟引擎,选择游戏类型,点击“下一步”,选择模板里面的“第三人称游戏“或者“第三人称蓝图”,点击下一步,选择蓝图,更改文件存放位置,和项目名称,点击“创建项目”

(1)、选择游戏类型,点击“下一步”

(2)、选择模板里面的“第三人称游戏“或者“第三人称蓝图”,点击下一步

(3)、选择蓝图,更改文件存放位置,和项目名称,点击“创建项目”,位置和名称自己命名,我这里是默认,还没有改

2、在虚幻编辑器的主菜单中选择

(1) 编辑(Edit) > 插件(Plugins)。

(2)在图像(Graphics) 下找到 像素流送(Pixel Streaming) 插件并勾选 启用(Enabled) 框。建议直接搜,比找要快

(3)点击是(Yes) 确认。

(4)点击立即重启(Restart Now) 重启项目并应用修改。

(5)返回虚幻编辑器,从主菜单中选择 编辑(Edit) > 项目设置(Project Settings)。 

(6)如果项目中有角色,而您希望启用触控设备的输入(如手机和平板电脑)在关卡中移动此角色,则可能需要在屏幕上显示触摸控制器。

        在 引擎(Engine) > 输入类型(Input category) 下启用 固定显示触控界面(Always Show Touch Interface) 设置。

此项为任选,并非所有项目强制要求。然而,在第三人称面板之类的项目中,这能确保拥有触控设备的用户能对流送的应用程序进行控制(前提是项目的玩家控制器支持输入)。

(7)在主菜单中选择 编辑(Edit) > 编辑器偏好(Editor Preferences...)

(8)在关卡编辑器(Level Editor) > 播放(Play) 中找到 额外启动参数(Additional Launch Parameters) 设置,并将其值设为 -AudioMixer -PixelStreamingIP=localhost -PixelStreamingPort=8888。

(9)打包Windows项目。从虚幻编辑器的主菜单中选择文件(Files) > 打包项目(Package Project) > Windows > Windows (64-bit)。

(10)设置打包文件放置目录,点击选择文件夹(Select Folder)。

(11)虚幻编辑器将开始打包进程。

(12)打包进程完毕后,前往上方第6步中选择的文件夹。将找到一个名为 WindowsNoEditor 的文件夹,其中的内容类似于下图:

(13) 每次启动打包应用程序时,均需要传递与第9步中一样的命令行标签。执行此操作的一种方法是设置一个快捷方式:

1-1 按住 Alt 键并拖动 .exe 文件即可在相同文件夹中(或在其他任意处)新建一个快捷方式。

1-2 右键点击快捷方式并从上下文菜单中选择属性(Properties)。

    1-3 在 *快捷方式属性(Shortcut Properties)** 窗口的 **快捷方式(Shortcut)** 选项卡中,在 **目标(Target)** 域的末尾附加文本 `-AudioMixer -PixelStreamingIP=localhost -PixelStreamingPort=8888` 并点击 **确认**。注意原先的和要添加的内容之间有个空格

(14) 双击启动这个快捷方式文件,即可开始流送渲染帧和音频。

3 启动服务器

        在这个步骤中将开启网络服务,在虚幻引擎应用程序和客户端浏览器间建立点对点的连接。

        以下步骤假设你使用的是Windows系统。不过,Linux系统上的操作方式相同,唯一不同在于你要执行 SignallingWebServer\platform_scripts\cmd\bash 目录中的脚本。

(1)在虚幻引擎安装文件夹中,在Samples/PixelStreaming/WebServers/SignallingWebServer 中找到信令服务器的路径。

(2)为了准备信令服务器,首先以管理员身份打开PowerShell并运行 SignallingWebServer\platform_scripts\cmd\setup.ps1。这会安装所有需要的依赖项。

(3)运行SignallingWebServer\platform_scripts\cmd\Start_SignallingServer.ps1 开启信令服务器。首次运行服务器时其将下载需要的所有依赖性。服务器启动,能够接收连接后,控制台窗口中将出现以下命令行:

---官网如上操作:实际上操作如下:

(1)在虚幻引擎打包文件夹中,在Samples/PixelStreaming/WebServers/SignallingWebServer 中找到信令服务器的路径。

(2)安装所有需要的依赖项,准备信令服务器,双击运行D:\ue\WindowsNoEditor\Engine\Source\Programs\PixelStreaming\WebServers\SignallingWebServer下的run.bat,等待变安装好了依赖并启动了服务器。这里可以查看代码就能明白双击run.bat文件调用了安装好了依赖并启动了服务器的命令,黑框里面显示如下,即为成功

WebSocket listening to Streamer connections on :8888 WebSocket listening to Players connections on :80 Http listening on *: 80

---实践结束

        接上步骤后,现在用上一节中创建的快捷方式来启动虚幻引擎应用程序(双击启动这个快捷方式文件)。或者,如果你习惯用命令行来启动应用,请执行以下命令:(重新定义bat的方式启动)

::MyPixelStreamingApplication.exe -PixelStreamingIP=127.0.0.1 -PixelStreamingPort=8888
@echo off
start MyPixelStreamingApplication.exe -PixelStreamingIP=127.0.0.1 -PixelStreamingPort=8888

::start WindowsNoEditor/PixStreamServer.exe -ResX=1920 -ResY=1080 -PixelStreamingIP=localhost -PixelStreamingPort=8888 -log -RenderOffScreen
 
::参数解析
::ResX、ResY设置窗口的分辨率
::PixelStreamingIP 信令服务的ip
::PixelStreamingPort 信令服务的端口
::log 日志
::RenderOffScreen 是否显示ue4服务窗口

        为便于使用,打包虚幻引擎应用程序时这些服务器也会被复制到包含打包可执行文件的文件夹中。他们位于上述路径下的 Engine 子文件夹中。可从该处启动服务器,而非从虚幻引擎安装文件夹中启动。

        须注意:如要修改这些文件夹中的任意文件(尤其是信令和Web服务器的播放器页面或配置文件),需要在原始位置中进行修改,如果在打包文件夹中进行修改,下次打包应用程序时修改可能会被覆盖。

最终结果:

当虚幻应用程序运行连接到信令服务器和Web服务器时,应该可以在信令和Web服务器打开的控制台窗口中看到以下输出行:

Streamer connected: ::1

        这说明虚幻引擎应用程序已经启用了像素流送插件,前端信令和Web服务器也已准备好将客户端连接到虚幻引擎应用程序。

        如有必要,你可以分别中断并重启虚幻引擎应用程序及信令和Web服务器。只要它们同时运行,就能够自动重连。

        此时必要的设置均已在电脑上完成。现在只需连接到浏览器。

4 连接

        在该步骤中将把多个不同设备上运行的网络浏览器连接到像素流送播放。

(1)控制虚幻引擎应用程序:在运行虚幻引擎应用程序的电脑上按下Alt+Tab组合键,将聚焦从虚幻引擎应用程序上移开,然后启动一个支持的Web浏览器(Google Chrome和Mozilla Firefox是不错的选择)。

(2)在浏览器地址栏中导航至 http://127.0.0.1。这是本地机的IP地址,因此请求应由信令服务器进行处理:

(3)点击页面进行连接,然后再次点击"播放(Play)"按钮开始流送。 

(4)现在便会连接到应用程序,渲染输出将流入播放器Web页面中:

     默认的播放器页面已进行设置,将把键盘、鼠标和触控屏输入发送到虚幻引擎,以便控制应用程序并前往各处,就像直接在应用程序进行控制一样。 

(5)点击窗口右侧的 + 按钮,展开流送控制的一些内置选项:展开流送控制的一些内置选项:

设置

描述

踢除其他所有玩家(Kick all other players)

让信令和Web服务器及虚幻引擎应用程序断开除当前浏览器之外所有其他浏览器的连接。

扩大显示以填充窗口(Enlarge Display to Fill Window)

确认媒体播放器是否应调整大小来匹配浏览器窗口的当前大小,还是应维持固定大小和位置。

质量控制所有权(Quality control ownership)

让像素流送插件的编码器使用当前的浏览器连接来确定可用的带宽,进而确定流送编码的质量。

虽然像素流送将使流送质量匹配可用的带宽,但像素流送插件只会对视频帧进行一次编码。此编码用于所有客户端。因此只有一个客户端连接可以"拥有"用于自适应流送的质量。如其他客户端与服务器之间的连接更优,则其显示的流送质量可能会低于预期。从另一方面来讲,如其他客户端与服务器之间的连接更差,则其可能会出现卡顿现象。

每次有新浏览器连接时,其将默认获得流送的所有权。使用此勾选框可以让其他连接的浏览器重新获得拥有权。

显示数据(Show Stats)

显示浏览器和虚幻引擎应用程序之间连接的统计数据。

查看信令Web服务器文件夹中 player.htm 和 app.js 文件的内容,了解这些控制方法的实现方式。

(6)现在找到网络中的其他电脑和/或移动设备。重复相同步骤,其他设备需要在同网络下,输入电脑的ip地址。

最终结果:在电脑上运行虚幻引擎的实例后,即可通过本地网络在多台设备上播放媒体流送。关卡的内容均在最初的电脑上进行渲染,但连接的每台设备均能看到相同的图像。

连接的所有设备默认均通过虚幻引擎应用程序共享控制,发送所有键盘、鼠标和触控屏输入。

四、遇到的问题

问题1:这里的条件具备后,执行到打包时提示无法找到xxx.uproject文件

  • 切换虚拟引擎版本-

          我试过4.26及以上的版本都有这样的错,而后虚拟引擎版本4.24没有这个错了

问题2:再次打包时发现报错,提示安装vs2017

  • 安装visualstudio2017-

          安装地址感谢您下载 Visual Studio - Visual Studio,进入变自动下载

        官网安装注意事项 https://docs.unrealengine.com/4.27/zh-CN/ProductionPipelines/DevelopmentSetup/VisualStudioSetup/

问题3:双击run.bat报错或者黑框闪退

  • 检测node的包管理器-

        安装依赖的时候会报错,可能和npm包管理器有关,检查包管理器或者修改setup.bat文件中代码,改为yarn来安装

问题4:点击click start时候,ue4服务器闪退并提示如下图所示:

  • UE4.26像素流插件PixelStreaming使用Chrome浏览器访问时的Crash问题-

        查看资料UE4.23/4.26像素流插件PixelStreaming使用Chrome访问时的Crash问题 - 知乎说添加 this.cfg.offerExtmapAllowMixed = false;和在webRtcPlayer.js(我的代码)对应130行添加下行代码offer.sdp = offer.sdp.replace(/(a=extmap-allow-mixed)\r\n/gm, "");

        然而在文档中已经存在这两句了,没啥大用处,我便继续找,终于找到了方法:

        在cirrus.js文件中添加 _RemoveExtmapAllowMixed(msg);// 替换sdp协议内新内容,再在文件的末尾添加_RemoveExtmapAllowMixed方法。

if (msg.type == 'offer') {
    console.log(`<- player ${playerId}: offer`);
    _RemoveExtmapAllowMixed(msg);// 替换sdp协议内新内容
    msg.playerId = playerId;
    streamer.send(JSON.stringify(msg));
}

 完整的一段代码

playerServer.on('connection', function (ws, req) {
    // Reject connection if streamer is not connected
    if (!streamer || streamer.readyState != 1 /* OPEN */) {
        ws.close(1013 /* Try again later */, 'Streamer is not connected');
        return;
    }
 
    let playerId = ++nextPlayerId;
    console.log(`player ${playerId} (${req.connection.remoteAddress}) connected`);
    players.set(playerId, { ws: ws, id: playerId });
 
    function sendPlayersCount() {
        let playerCountMsg = JSON.stringify({ type: 'playerCount', count: players.size });
        for (let p of players.values()) {
            p.ws.send(playerCountMsg);
        }
    }
    
    ws.on('message', function (msg) {
        console.logColor(logging.Blue, `<- player ${playerId}: ${msg}`);
 
        try {
            msg = JSON.parse(msg);
        } catch (err) {
            console.error(`Cannot parse player ${playerId} message: ${err}`);
            ws.close(1008, 'Cannot parse');
            return;
        }
 
        if (msg.type == 'offer') {
            console.log(`<- player ${playerId}: offer`);
            _RemoveExtmapAllowMixed(msg);// 替换sdp协议内新内容
            msg.playerId = playerId;
            streamer.send(JSON.stringify(msg));
        } else if (msg.type == 'iceCandidate') {
            console.log(`<- player ${playerId}: iceCandidate`);
            msg.playerId = playerId;
            streamer.send(JSON.stringify(msg));
        } else if (msg.type == 'stats') {
            console.log(`<- player ${playerId}: stats\n${msg.data}`);
        } else if (msg.type == 'kick') {
            let playersCopy = new Map(players);
            for (let p of playersCopy.values()) {
                if (p.id != playerId) {
                    console.log(`kicking player ${p.id}`)
                    p.ws.close(4000, 'kicked');
                }
            }
        } else {
            console.error(`<- player ${playerId}: unsupported message type: ${msg.type}`);
            ws.close(1008, 'Unsupported message type');
            return;
        }
    });
 
    function onPlayerDisconnected() {
        players.delete(playerId);
        streamer.send(JSON.stringify({type: 'playerDisconnected', playerId: playerId}));
        sendPlayerDisconnectedToFrontend();
        sendPlayerDisconnectedToMatchmaker();
        sendPlayersCount();
    }
 
    ws.on('close', function(code, reason) {
        console.logColor(logging.Yellow, `player ${playerId} connection closed: ${code} - ${reason}`);
        onPlayerDisconnected();
    });
 
    ws.on('error', function(error) {
        console.error(`player ${playerId} connection error: ${error}`);
        ws.close(1006 /* abnormal closure */, error);
        onPlayerDisconnected();
    });
 
    sendPlayerConnectedToFrontend();
    sendPlayerConnectedToMatchmaker();
 
    ws.send(JSON.stringify(clientConfig));
 
    sendPlayersCount();
});

文件末尾添加的_RemoveExtmapAllowMixed方法

/**
 * sdp协议更新的方法
 * @param desc offer内容
 * @returns {string}
 */
 function _RemoveExtmapAllowMixed(desc) {
    if (desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
        const sdp = desc.sdp.split('\n').filter((line) => {
            return line.trim() !== 'a=extmap-allow-mixed';
        }).join('\n');
        desc.sdp = sdp;
        return sdp;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_55173487/article/details/126231595
今日推荐