WebRTC 上的開源雲端遊戲:p2p、多人、零延遲

WebRTC 上的開源雲端遊戲:p2p、多人、零延遲
軟體即服務、基礎設施即服務、平台即服務、通訊平台即服務、視訊會議即服務、雲端遊戲即服務呢? 目前已經有一些創建雲端遊戲(Cloud Gaming)的嘗試,例如Google最近推出的Stadia。 斯塔迪亞 WebRTC 不陌生,但是其他人可以用同樣的方式使用WebRTC嗎?

Thanh Nguyen 決定在他的開源專案 CloudRetro 上測試這個機會。 CloudRetro基於Pion, 受歡迎的 基於Go的WebRTC庫(感謝 如圖所示 感謝 Pion 開發團隊在準備本文時提供的協助)。 在這篇文章中,Thanh 概述了他的專案架構,並討論了他在工作中學到的有用的東西以及遇到的挑戰。

條目

去年,當Google宣布推出 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 滿足了我作為復古遊戲玩家所期望的所有用例。 不過,我認為該項目中有很多方面可以改進,例如使網路更加可靠和性能更高,提供更高品質的遊戲圖形,或在用戶之間共享遊戲的能力。 我正在為此努力。 請關注 項目 並支持它,如果你喜歡它。

來源: www.habr.com

添加評論