WebRTC 上的开源云游戏:p2p、多人、零延迟

WebRTC 上的开源云游戏:p2p、多人、零延迟
软件即服务、基础设施即服务、平台即服务、通信平台即服务、视频会议即服务、云游戏即服务呢? 目前已经有一些创建云游戏(Cloud Gaming)的尝试,例如谷歌最近推出的Stadia。 斯塔迪亚 WebRTC 并不陌生,但是其他人可以用同样的方式使用WebRTC吗?

Thanh Nguyen 决定在他的开源项目 CloudRetro 上测试这个机会。 CloudRetro基于Pion, 受欢迎的 基于Go的WebRTC库(感谢 秀努 感谢 Pion 开发团队在准备本文时提供的帮助)。 在这篇文章中,Thanh 概述了他的项目架构,并讨论了他在工作中学到的有用的东西以及遇到的挑战。

输入

去年,当谷歌宣布推出 Stadia 时,我大吃一惊。 这个想法是如此独特和创新,以至于我一直想知道如何利用现有技术实现这一点。 更好地理解这个主题的愿望促使我创建了自己的开源云游戏版本。 结果简直太棒了。 下面我来分享一下我这一年的工作过程 项目.

TLDR:带有亮点的短幻灯片版本

为什么云游戏是未来

我相信云游戏很快将不仅成为游戏的下一代,而且还将成为计算机科学其他领域的下一代。 云游戏是客户端/服务器模型的顶峰。 该模型通过在远程服务器上托管游戏逻辑并将图像/音频流传输到客户端,最大化后端管理并最小化前端工作。 服务器执行繁重的处理,因此客户端不再受硬件限制的支配。

Google Stadia 本质上就是让你玩游戏 AAA级游戏 (即高端大片游戏)在 YouTube 等界面上。 同样的方法可以应用于其他重度离线应用程序,例如操作系统或 2D/3D 图形设计等。 这样我们就可以跨多个平台在低规格设备上一致地运行它们。

WebRTC 上的开源云游戏:p2p、多人、零延迟
这项技术的未来:想象一下,如果 Microsoft Windows 10 在 Chrome 浏览器上运行会怎样?

云游戏在技术上具有挑战性

游戏是需要持续、快速的用户响应的罕见领域之一。 如果偶尔我们在点击页面时遇到 2 秒的延迟,这是可以接受的。 实时视频流往往会滞后几秒钟,但仍然提供合理的可用性。 但如果游戏经常出现500ms的延迟,那根本就玩不了。 我们的目标是实现极低的延迟,以便输入和媒体之间的差距尽可能小。 因此,传统的视频流方法在这里不适用。

WebRTC 上的开源云游戏:p2p、多人、零延迟
通用云游戏模板

开源项目CloudRetro

我决定创建一个云游戏的测试样本,看看在如此严格的网络限制下这一切是否可行。 我选择 Golang 来进行概念验证,因为它是我最熟悉的语言,并且由于许多其他原因非常适合此实现,正如我后来发现的那样。 Go很简单,发展也很快; Go 中的通道非常适合管理多线程。

项目 云复古 是一个针对复古游戏的开源云游戏服务。 该项目的目标是为传统复古游戏带来最舒适的游戏体验,并添加多人游戏。
您可以在这里了解有关该项目的更多信息: https://github.com/giongto35/cloud-game.

云复古功能

CloudRetro用复古游戏来展示云游戏的强大。 这可以让您获得许多独特的游戏体验。

  • 游戏的便携性
    • 打开页面即时播放; 无需下载或安装
    • 在移动浏览器中工作,因此不需要任何软件来运行它

  • 游戏会话可以在多个设备上共享,并存储在云端供您下次登录时使用
  • 游戏可以流式传输,也可以由多个用户同时玩:
    • 像 TwitchPlayPokemon 一样的集体游戏,只是更跨平台、更实时
    • 离线游戏在线。 许多用户无需设置网络即可玩游戏。 Samurai Shodown 现在可以由 2 名玩家通过 CloudRetro 网络进行游戏

    WebRTC 上的开源云游戏:p2p、多人、零延迟
    不同设备上的在线多人游戏演示版

    基础设施

    需求和技术栈

    以下是我在开始项目之前设定的要求列表。

    1. 一名玩家
    这个要求在这里可能看起来不太重要或明显,但它是我的关键要点之一,它允许云游戏尽可能远离传统的流媒体服务。 如果我们专注于单人游戏,我们可以摆脱集中式服务器或 CDN,因为我们不必向大众进行流媒体播放。 服务流不是将流上传到接收服务器或将数据包传递到集中式 WebSocket 服务器,而是通过点对点 WebRTC 连接直接传送给用户。

    2. 低延迟媒体流
    在阅读有关 Stadia 的文章时,我经常看到一些文章中提到 WebRTC。 我意识到 WebRTC 是一项出色的技术,非常适合在云游戏中使用。 WebRTC 是一个通过简单的 API 为网络浏览器和移动应用程序提供实时通信的项目。 它提供点对点连接,针对媒体进行了优化,并具有内置标准编解码器,例如 VP8 和 H264。

    我优先考虑确保最佳的用户体验,而不是维持高质量的图形。 算法中有些损失是可以接受的。 Google Stadia 还有一个额外的步骤,即减小服务器上的图像大小,并且在将帧传输到同行之前将其放大到更高的质量。

    3.具有地理路由的分布式基础设施
    无论压缩算法和代码如何优化,网络仍然是对延迟影响最大的决定因素。 该架构必须有一种机制来配对距离用户最近的服务器,以减少往返时间 (RTT)。 该架构必须有 1 个协调器和多个分布在世界各地的流媒体服务器:美国西部、美国东部、欧洲、新加坡、中国。 所有流媒体服务器必须完全隔离。 当服务器加入或离开网络时,系统可以调整其分布。 因此,对于大流量,添加额外的服务器可以实现水平扩展。

    4. 浏览器兼容性
    当云游戏对用户的要求最少时,云游戏才能达到最佳状态。 这意味着可以在浏览器中运行。 浏览器有助于使用户获得尽可能舒适的游戏体验,从而使他们无需安装软件和硬件。 浏览器还有助于提供移动版本和桌面版本之间的跨平台功能。 幸运的是,WebRTC 得到了多种浏览器的良好支持。

    5. 游戏界面和服务清晰分离
    我将云游戏服务视为一个平台。 每个人都应该能够将任何东西连接到平台。 现在我已经集成了 LibRetro 使用云游戏服务,因为 LibRetro 为 SNES、GBA、PS 等复古游戏提供了漂亮的游戏模拟器界面。

    6. 多人游戏、人群游戏和游戏外部链接(深层链接)的房间
    CloudRetro 支持许多新的游戏玩法,例如复古游戏的 CrowdPlay 和在线多人游戏。 如果多个用户在不同的计算机上打开相同的深层链接,他们将看到相同的正在运行的游戏,甚至能够加入它。

    此外,游戏状态存储在云存储中。 这允许用户随时在任何其他设备上继续玩游戏。

    7. 水平缩放
    与当今的任何 SAAS 一样,云游戏必须设计为可水平扩展。 协调员-工作人员设计允许您添加更多工作人员以服务更多流量。

    8. 无法连接到一处云
    CloudRetro的基础设施托管在不同地区的不同云提供商(Digital Ocean、阿里巴巴、定制提供商)上。 我允许在基础设施的 Docker 容器中运行,并使用 bash 脚本配置网络设置,以避免被锁定到单个云提供商。 通过将其与WebRTC中的NAT穿越相结合,我们可以灵活地在任何云平台甚至任何用户的机器上部署CloudRetro。

    建筑设计

    工人: (或上面提到的流媒体服务器)乘以游戏,运行编码管道,并将编码媒体流传输给用户。 Worker实例分布在世界各地,每个Worker可以同时处理多个用户会话。

    协调员: 负责将新用户与最适合流媒体的工作人员配对。 协调器通过 WebSocket 与工作器交互。

    游戏状态存储: 所有游戏状态的中央远程存储。 该存储提供重要功能,例如远程保存/加载。

    WebRTC 上的开源云游戏:p2p、多人、零延迟
    CloudRetro顶层架构

    自定义脚本

    当新用户按照下图所示的步骤 1 和 2 打开 CloudRetro 时,将向第一页请求协调器以及可用工作人员列表。 此后,在步骤 3 中,客户端使用 HTTP ping 请求计算所有候选者的延迟。 然后,该延迟列表被发送回协调器,以便他可以确定最适合为用户服务的工作人员。 下面的步骤 4 创建游戏。 用户和分配的工作人员之间建立 WebRTC 流连接。
    WebRTC 上的开源云游戏:p2p、多人、零延迟
    获得访问权限后的用户脚本

    工人里面有什么

    游戏和流媒体管道独立存储在工作器内部,并通过接口交换信息。 目前,这种通信是通过传输内存中的数据来进行的 Golang 通道 在同一过程中。 下一个目标是隔离,即在另一个进程中独立启动游戏。

    WebRTC 上的开源云游戏:p2p、多人、零延迟
    工作组件的交互

    主要成分:

    • 的WebRTC: 接受用户输入并从服务器输出编码媒体的客户端组件。
    • 游戏模拟器: 游戏组件。 得益于 Libretro 库,系统能够在同一进程内运行游戏并在内部拦截媒体和输入流。
    • 游戏中的帧被捕获并发送给编码器。
    • 图像/音频编码器: 一个编码管道,它获取媒体帧,在后台对其进行编码,并输出编码的图像/音频。

    履行

    CloudRetro 依赖 WebRTC 作为其骨干技术,因此在深入探讨 Golang 实现的细节之前,我决定先谈谈 WebRTC 本身。 这是一项了不起的技术,极大地帮助我实现了流数据的亚秒级延迟。

    实现WebRTC

    WebRTC 旨在使用简单的 API 在本机移动应用程序和浏览器上提供高质量的点对点连接。

    NAT穿越

    WebRTC 以其 NAT 穿越功能而闻名。 WebRTC 专为点对点通信而设计。 其目标是找到最合适的直接路由,避免 NAT 网关和防火墙,通过一个称为 ICE。 作为此过程的一部分,WebRTC API 使用 STUN 服务器查找您的公共 IP 地址并将其转发到中继服务器() 当无法建立直接连接时。

    然而,CloudRetro 并没有充分利用这个功能。 它的点对点连接不存在于用户之间,而是存在于用户和云服务器之间。 该模型的服务器端比典型的用户设备具有更少的直接通信限制。 这允许您预先打开传入端口或直接使用公共 IP 地址,因为服务器不在 NAT 之后。

    之前我想把这个项目打造成一个云游戏的游戏分发平台。 这个想法是允许游戏创作者提供游戏和流媒体资源。 用户将直接与提供商交互。 在这种去中心化的方式下,CloudRetro只是一个将第三方流媒体资源连接到用户的框架,使其在不再托管时更具可扩展性。 WebRTC NAT Traversal在这里的作用非常重要,可以方便第三方流媒体资源上的点对点连接初始化,让创作者更容易连接网络。

    视频压缩

    视频压缩是管道中不可或缺的一部分,对于流畅的流程有很大贡献。 虽然没有必要了解 VP8/H264 视频编码的每个细节,但了解这些概念可以帮助您了解流视频速度选项、调试意外行为以及调整延迟。

    为流媒体服务压缩视频具有挑战性,因为算法必须确保总编码时间+网络传输时间+解码时间尽可能低。 此外,编码过程必须一致且连续。 某些编码权衡并不适用,例如,我们不能选择较长的编码时间而不是较小的文件大小和解码时间,或者使用不一致的压缩。

    视频压缩背后的想法是消除不必要的信息位,同时保持用户可接受的准确性水平。 除了对各个静态图像帧进行编码之外,该算法还根据前一帧和下一帧推断当前帧,因此仅发送它们的差异。 从 Pacman 的示例中可以看出,仅传输差分点。

    WebRTC 上的开源云游戏:p2p、多人、零延迟
    以Pacman为例的视频帧对比

    音频压缩

    同样,音频压缩算法会忽略人类无法感知的数据。 Opus 是目前性能最好的音频编解码器。 它旨在通过有序数据报协议(例如 RTP(实时传输协议))传输音频波。 它的延迟低于mp3和aac,并且质量更高。 延迟通常在5~66,5ms左右。

    Pion、Golang 中的 WebRTC

    介子 是一个将 WebRTC 引入 Golang 的开源项目。 Pion 不是通常对原生 C++ WebRTC 库的包装,而是 WebRTC 的原生 Golang 实现,具有更好的性能、Go 集成和 WebRTC 协议的版本控制。

    该库还支持使用许多出色的内置程序进行流式传输,延迟时间亚秒级。 它有自己的 STUN、DTLS、SCTP 等实现。 以及 QUIC 和 WebAssembly 的一些实验。 这个开源库本身就是一个非常好的学习资源,具有优秀的文档、网络协议实现和很酷的示例。

    Pion 社区由一位非常热情的创建者领导,非常活跃,正在进行大量关于 WebRTC 的高质量讨论。 如果您对这项技术感兴趣,请加入 http://pion.ly/slack – 你会学到很多新东西。

    用 Golang 编写 CloudRetro

    WebRTC 上的开源云游戏:p2p、多人、零延迟
    Go中worker的实现

    Go 频道的实际应用

    得益于 Go 优美的通道设计,事件流和并发的问题被大大简化。 如图所示,不同的 GoRoutine 有多个并行运行的组件。 每个组件管理其状态并通过通道进行通信。 Golang 的选择性断言强制游戏中每次(游戏滴答)处理一个原子事件。 这意味着该设计不需要锁定。 例如,当用户保存时,需要游戏状态的完整快照。 此状态应保持连续,登录直至保存完成。 在每个游戏周期中,后端只能处理保存或输入操作,从而使进程线程安全。

    func (e *gameEmulator) gameUpdate() {
    for {
    	select {
    		case <-e.saveOperation:
    			e.saveGameState()
    		case key := <-e.input:
    			e.updateGameState(key)
    		case <-e.done:
    			e.close()
    			return
    	}
        }
    }

    扇入/扇出

    这个 Golang 模板非常适合我的 CrowdPlay 和多玩家用例。 按照这种模式,一个房间中的所有用户输入都内置到中央入口通道中。 然后,游戏媒体会部署到同一房间中的所有用户。 这样我们就实现了不同用户的几个游戏会话之间游戏状态的划分。

    WebRTC 上的开源云游戏:p2p、多人、零延迟
    不同会话之间的同步

    Golang的缺点

    Golang 并不完美。 频道很慢。 与阻塞相比,Go Channel 只是一种处理并发和线程事件的更简单的方法,但 Channel 并没有提供最佳的性能。 通道下面有复杂的阻塞逻辑。 所以我对实现做了一些调整,在更换通道时重新应用锁和原子值来优化性能。

    此外,Golang 中的垃圾收集器是不受管理的,这有时会导致可疑的长时间暂停。 这极大地干扰了实时流应用。

    首席财务官

    该项目使用现有的开源 Golang VP8/H264 库进行媒体压缩,并使用 Libretro 进行游戏模拟器。 所有这些库都只是 Go 中 C 库的包装器,使用 首席财务官。 一些缺点列于 戴夫·切尼的这篇文章。 我遇到的问题:

    • 即使使用 Golang RecoveryCrash,也无法捕获 CGO 中的崩溃;
    • 当我们无法检测到 CGO 中的详细问题时,就无法识别性能瓶颈。

    结论

    我实现了了解云游戏服务并创建一个平台来帮助我与朋友在线玩怀旧复古游戏的目标。 如果没有 Pion 库和 Pion 社区的支持,这个项目就不可能实现。 我非常感谢它的深入发展。 WebRTC 和 Pion 提供的简单 API 确保了无缝集成。 我的第一个概念验证在同一周发布,尽管我事先并不了解点对点 (P2P) 通信。

    尽管易于集成,P2P 流确实是计算机科学中一个非常复杂的领域。 她必须处理长期存在的网络架构(例如 IP 和 NAT)的复杂性,以创建点对点会话。 在从事这个项目的过程中,我获得了很多有关网络和性能优化的宝贵知识,因此我鼓励大家尝试使用 WebRTC 构建 P2P 产品。

    CloudRetro 满足了我作为一名复古游戏玩家所期望的所有用例。 不过,我认为该项目中有很多方面可以改进,例如使网络更加可靠和高性能,提供更高质量的游戏图形,或者在用户之间共享游戏的能力。 我正在为此努力。 请关注 项目 并支持它,如果你喜欢它。

来源: habr.com

添加评论