PixelStreaming:基本概念与上手初探

PixelStreaming 是 UE_4.21 开始支持的一项技术,简单来说就是能够将游戏跑在服务器上,你可以通过浏览器来玩,玩家端不需要额外操作,只需要一个浏览器,所有的逻辑处理和渲染都在“云”端执行。它不仅仅只是一个插件 (虽然有 PixelStreamingPlugin 这个插件,但它只是PixelStreaming 实现中的一环),其实现具有一套独立与 UE 游戏的设计和组织方式。
本篇文章主要介绍 PixelStreaming 中的基本概念和使用 PixelStreaming 的 Demo 的实际体验。

来看一下 UE 自己的介绍:

Run your Unreal Engine application on a server in the cloud, and stream its rendered frames and audio to browsers and mobile devices over WebRTC.

PixelStreaming 的数据传输使用的是WebRTC,P2P 的连接延迟更低。

注意:本文所描述内容的测试引擎为 UE_4.22.3,目前 PixelStreaming 只能运行在 Windows 平台 (依赖于模块D3DX11NvVideoEncoder库)。

UE 提供的 PixelStreaming 展示案例的代码分成了几个部分:

  • PixelStreamingPlugin:提供渲染帧与音频数据的捕获与编码,并创建一个链接将音频和渲染帧的数据传递给 WebRTC 服务器,使用的是 UE 的插件方式编写和使用。
  • WebRTCProxy:应用 / 游戏与 WebServer 交流的中间层,作用是将游戏内编码好的渲染帧数据 (.h264) 与音频数据传递给 WebServer,并从 WebServer 上接收到的用户输入再传递给应用 / 游戏,从而可以从浏览器上控制游戏。使用 UE 的 Program 方式编写。
  • WebServer:PixelStreaming 设计的组织方式中,WebServer是由两个模块组成:SignalingWebServerMatchmaker,使用的是 nodejs 编写。
  • SignallingWebServer:启动一个 HTTP 服务,从 WebRTC 接收渲染帧和音频数据传递给浏览器客户端,并接收远程用户的输入 (键盘、鼠标等) 再传递给 WebRTC。
  • MatchMakerServer:如果只是启动一个游戏实例和单个的 SignallingWebServer,那么使用浏览器访问的玩家操作的都是同一个游戏实例,他们的键盘和鼠标操作都会相互干扰。解决这个的问题的办法使用MatchMakerServer,但是这种方式也是需要启动多套 游戏实例 /WebRTCProxy 以及SignallingWebServer,供外部用户每个人访问不同的游戏实例,使每个浏览器访问的客户端具有不同的连接。

用户访问单个实例的流程如下图:

后续我会逐个分析一下它们各自的代码实现(上周末的简单分析了一下 PixelStreamingPlugin 的实现),这篇文章的目的是介绍基本概念和简单的 PixelStreamingDemo 上手。

本篇文章的参考资料主要是 UE 文档库中对于 PixelStreaming 的介绍,以及我读代码了解到的实现方式,目前官方文档页面的描述不是特别详细,有些细节和可以用的参数命令没有提及,还是看代码靠谱。

PixelStreamingDemo

基本概念介绍完毕,下面进入实际的上手环节。

Launch Game

首先创建一个 C++ 项目,我选择了一个第三人称模板:

创建完成之后,打开 Edit-Plugins 搜索启用PixelStreaming:

然后重启编辑器。

与 UE 的官网介绍 (Getting Started with Pixel Streaming) 的需要打包不同,只要使用 Standalone 模式启动项目就可以加载 PixelStreamingPlugin,这样可以省去每次都要打包的麻烦。
为了方便添加启动参数,可以使用我之前写的一个工具:UE4 Launcher
并为其添加启动参数-game/-log/-AudioMixer(PixelStreamingPlugin 需要这个参数):

注:PixelStreamingPlugin 支持启动时指定要连接的 WebRTCProxy 的地址与端口,可以在上面的启动参数列表中添加。

  • -PixelStreamingIP=<value>:指定 WebRTCProxy 服务器的地址,默认为0.0.0.0.
  • -PixelStreamingPort=<value>:指定 WebRTCProxy 服务器的端口,默认为8124.

然后点击 Launch Configuration 即可以 Standalone 模式启动项目。

启动之后可以从 Log 窗口中看到 PixelStreaming 的 Log 输出:

WebRTCProxy

经过上面的操作游戏启动完成之后,可以启动 WebRTC 了,打开引擎目录:

1
Engine\Source\Programs\PixelStreaming\WebRTCProxy\bin

可以看到目录下有五个文件:

1
2
3
4
5
2019/07/29  01:21               110 Start_AWS_WebRTCProxy.bat
2019/07/29 01:21 473 Start_AWS_WebRTCProxy.ps1
2019/07/29 01:21 38 Start_WebRTCProxy.bat
2019/07/29 01:21 18,032,016 WebRTCProxy.exe
2019/07/29 01:21 15,511,552 WebRTCProxy.pdb

需要做的只有双击 Start_WebRTCProxy.bat 启动(它里面也仅仅只是启动了WebRTCProxy.exe).

启动之后可以看到连接信息:

此时在打开游戏项目的 Log 窗口可以看到如下内容:

1
[2019.07.30-08.18.49:324][758]PixelStreamingNet: Accepted connection from WebRTC Proxy: 127.0.0.1:2703

表明 PixelStreamingPlugin 与 WebRTCProxy 已经连接成功了。

WebRTCProxy也可以指定游戏内 PixelStreamingPlugin 设定的端口,以及 SignallingWebServer 的地址 / 端口,可以通过 -help 参数来查看 WebRTCProxy 支持的参数(这部分的实现代码在 Engine/Source/Programs/PixelStreaming/WebRTCProxy/src/WebRTCProxy.cpp 中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
E:\UnrealEngine\Epic\UE_4.22\Engine\Source\Programs\PixelStreaming\WebRTCProxy\bin>WebRTCProxy.exe -help
WebRTCProxy
Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

Parameters:

-help
Shows this help

-Cirrus=<IP:Port>
The Cirrus server to connect to. If not specified. it defaults to 127.0.0.1:8888

-StunServer=<IP:Port>
Stun server to use.

-UE4Port=<Port>
The port UE4 is listening on

-AutoSetBitrate
If specified, it forcibly sends a bitrate request to UE4 once a client gets
quality control ownership

-PlanB
If specified, it will use PlanB sdp semantics. Default is UnifiedPlan.

-dbgwindow=[Proxy|WebRTC|All|None]
If running under the debugger (e.g: Visual Studio), it specifies what logs to
send to the Output Window.
Proxy - Only logs from WebRTCProxy itself will be displayed.
WebRTC - Only logs from WebRTC internals will be displayed.
All - (Default) Both WebRTCProxy and WebRTC internal logs are displayed.
None - No logs sent to the Output Window

-LocalTime
If specified, it will use local time in logging, instead of UTC.

其中:

  • -Cirrus=<IP:Port>用来指定与 SignallingWebServer 通信的端口(默认为127.0.0.1:8888).
  • -UEPort=<Port>:用来指定与该 WebRTCProxy 关联的 UE 应用的端口(PixelStreamingPlugin 中的连接端口,默认为127.0.0.1:8124)

SignallingWebServer

当游戏与 WebRTCProxy 都启动完毕之后,需要开始启动真正供浏览器可访问的 HTTP 服务器了。

首先需要安装 node,下载安装之后将其添加到系统的 PATH 路径,通过命令node -vnpm -v测试是否安装成功:

1
2
3
4
C:\Users\imzlp\Desktop>node -v
v10.16.0
C:\Users\imzlp\Desktop>npm -v
6.9.0

如上图则没有问题,可以启动 SignallingWebServer 了,打开引擎目录:

1
Engine\Source\Programs\PixelStreaming\WebServers\SignallingWebServer

里面有一堆的文件,需要启动的是 run.bat 或者 runNoSetup.bat(他们两个唯一的区别为是否执行npm install),如果你是第一次打开,则 以管理员权限 启动run.bat,后续直接启动runNoSetup.bat 即可

经过一堆的 npm 包的下载与安装之后,启动成功:

此时打开 WebRTCProxy 的窗口界面,可以看到以下内容:

此时所有需要启动的服务都已启动完毕。

注:SignallingWebServer默认连接 127.0.0.1:8888 的 WebRTCProxy,并开启一个 80 端口的 HTTP 服务。

  • 使用 --proxyPort <Port> 可以修改监听的 WebRTCProxy 端口,或者写在 SignallingWebSerber/config.json 配置下。
  • 使用 --httpPort <Port> 可以修改启动的 HTTP 服务器的端口。

打开浏览器输入 http://127.0.0.1(使用的是默认端口 80,不然需要指定端口号https://127.0.0.1:xxxx) 就可以看到以下界面:

鼠标点击即可进入游戏画面:

其他的移动设备如果在同一个网段,也是可以直接连接的:

我也录了一个简单的视频(在 ipad 上操作):

MatchMakerServer

在文章的最开始,我简单说明了一下 MatchMakerServer 的用途,下面简单说一下如何做。

先来看 UE 对 MatchMakerServer 的职责图:

用法为:在启动单个的 SignallingWebServer 时,指定参数启用Matchmaker

1
runNoSetup.bat --UseMatchmaker --matchmakeAddress 127.0.0.1 --matchmakerPort 9999

然后启动MatchmakerServer,找到以下路径:

1
Engine\Source\Programs\PixelStreaming\WebServers\Matchmaker

直接启动 run.bat 的默认 httpPort 为 90,matchmakerPort为 9999,即上面运行 SignallingWebServer 时指定的--matchmakerPort 9999,可以自己使用参数替换。

1
run.bat --httpPort 88 --matchmakerPort 9988

下面使用的是默认的 httpPort/matchmakePort 端口

然后访问 http:127.0.0.1:90,可以看到与单独启动的SignallingWebServer 一样,但是不允许其他的客户端再加入到当前的会话中来,会提示WARNING: No empty Cirrus servers are available

除非同时运行多套 游戏 /WebRTCProxy/SignallingWebServer,使每个玩家独占一套不相互干扰,这也是MatchmakerServer 的作用所在。

结语

Pixel Streaming 这种“云”游戏的思路很好,但是目前的硬件环境和带宽瓶颈还是很大的问题,在我测试体验的过程中,局域网连接比较流畅,在设定最高 60fps 的情况下可以跑到满帧,但是也是会出现偶尔掉帧的情况。

Google 也推出了云游戏平台Stadia,但是离真的可以落地还很远,但是目前做一些行业应用还是可以的(比如雪佛兰的汽车展示2020 CORVETTE STINGRAY)。