Cloud als Dienst, Infrastruktur als Dienst, Plattform als Dienst, Kommunikationsplattform als Dienst, Videokonferenzen als Dienst, und was ist mit Cloud Gaming als Dienst? Es gab bereits mehrere Versuche zur Schaffung von Cloud-Gaming, wie zum Beispiel Stadia, das kürzlich von Google lanciert wurde. Stadia , aber können andere WebRTC auch so verwenden?
Thanh Nguyen beschloss, diese Möglichkeit in seinem Open-Source-Projekt CloudRetro zu testen. CloudRetro basiert auf Pion, WebRTC-Bibliothek auf Basis von Go (danke aus dem Pion-Entwicklerteam für die Unterstützung bei der Erstellung dieses Artikels). In diesem Artikel gibt Thanh einen Überblick über die Architektur seines Projekts und erzählt, was er dabei gelernt hat und mit welchen Herausforderungen er während der Arbeit konfrontiert war.
Einleitung
Als Google Stadia im letzten Jahr ankündigte, war ich einfach überwältigt. Die Idee ist so einzigartig und innovativ, dass ich mich ständig fragte, wie so etwas mit bestehenden Technologien überhaupt möglich sein kann. Der Wunsch, dieses Thema besser zu verstehen, motivierte mich, meine eigene Version eines Open-Source-Cloud-Games zu erstellen. Das Ergebnis war einfach fantastisch. Im Folgenden möchte ich den Prozess meiner einjährigen .
TLDR: Kurze Slide-Version mit den wichtigsten Punkten
Warum die Zukunft den Cloud-Spielen gehört
Ich glaube, dass Cloud Gaming bald eine neue Generation von nicht nur Spielen, sondern auch anderen Bereichen der Informatik darstellen wird. Cloud-Gaming ist der Gipfel des Client/Server-Modells. Dieses Modell maximiert die Kontrolle über das Backend und minimiert die Arbeit des Frontends, indem die Spiel-Logik auf einem entfernten Server ausgeführt und Bild-/Audio-Streams an den Client gesendet werden. Der Server führt die rechenintensive Verarbeitung durch, weshalb der Client nicht mehr von Hardware-Limitierungen abhängig ist.
Google Stadia ermöglicht es im Grunde, AAA-Spiele Die Zukunft dieser Technologie: Stellen Sie sich vor, Microsoft Windows 10 würde im Browser Chrome laufen.

Cloud-Gaming ist technisch komplex.
Cloud-Gaming ist technisch anspruchsvoll.
Gaming ist eines der wenigen Bereiche, in denen eine ständige schnelle Reaktion des Benutzers erforderlich ist. Wenn wir gelegentlich eine Verzögerung von 2 Sekunden beim Klicken auf der Seite feststellen, ist das akzeptabel. Live-Streaming-Videos hinken in der Regel um einige Sekunden hinterher, bieten aber dennoch genügend Komfort bei der Nutzung. Wenn das Spiel jedoch häufig um 500 ms verzögert wird, ist es einfach unmöglich zu spielen. Unser Ziel ist es, eine extrem niedrige Latenz zu erreichen, sodass der Abstand zwischen Eingabe und Medien so gering wie möglich ist. Daher ist der traditionelle Ansatz für Streaming-Videos hier nicht anwendbar.

Allgemeines Muster des Cloud-Gaming
Open-Source-Projekt CloudRetro
Ich habe beschlossen, ein Testmuster für Cloud-Gaming zu erstellen, um zu überprüfen, ob all dies unter solch strengen Netzwerkanforderungen möglich ist. Zur Konzeptüberprüfung habe ich Golang gewählt, da es die mir vertrauteste Sprache ist und sich aus vielen anderen Gründen gut für diese Implementierung eignet, wie sich später herausstellte. Go ist einfach und entwickelt sich sehr schnell; die Kanäle in Go eignen sich hervorragend für die Verwaltung von Nebenläufigkeit.
Projekt – ein Cloud-Gaming-Dienst mit offenem Quellcode für Retro-Spiele. Das Ziel des Projekts ist es, den traditionellen Retro-Spielen die komfortabelsten Spielerlebnisse zu verleihen und Multiplayer-Funktionen hinzuzufügen.
Details zum Projekt finden Sie hier: .
Funktionalität von CloudRetro
Um die gesamte Power des Cloud-Gamings zu demonstrieren, werden in CloudRetro Retro-Spiele verwendet. Dadurch entstehen viele einzigartige Spielerlebnisse.
- Portabilität des Spiels
- Sofortige Wiedergabe beim Öffnen der Seite; kein Download und keine Installation erforderlich
- Funktioniert im mobilen Browser, sodass keine Softwareinstallation erforderlich ist
- Spielsitzungen können auf mehreren Geräten zusammen genutzt und in der Cloud für den nächsten Login gespeichert werden
- Das Spiel kann gestreamt werden, und gleichzeitig können mehrere Benutzer spielen:
- Crowdplay ähnlich wie TwitchPlayPokemon, nur plattformübergreifender und in Echtzeit-gesteuerter
- Offline-Spiele online. Viele Benutzer können ohne Netzwerksetup spielen. In Samurai Shodown können jetzt 2 Spieler über das CloudRetro-Netzwerk spielen.

Demo-Version eines Mehrspieler-Online-Spiels auf verschiedenen GerätenInfrastruktur
Anforderungen und Technologie-Stack
Im Folgenden finden Sie eine Liste der Anforderungen, die ich vor Beginn des Projekts festgelegt habe.
1. Ein Spieler
Diese Anforderung mag hier nicht allzu wichtig und offensichtlich erscheinen, aber sie ist einer meiner entscheidenden Erkenntnisse, denn sie ermöglicht es Cloud-Gaming, sich so weit wie möglich von traditionellen Streaming-Diensten zu entfernen. Wenn wir uns auf das Einzelspieler-Spiel konzentrieren, können wir auf einen zentralisierten Server oder CDN verzichten, da wir nicht auf Masse streamen müssen. Anstatt Streams auf einen datengestützten Server hochzuladen oder Pakete an einen zentralen WebSocket-Server zu übertragen, werden die Service-Streams direkt über eine Peer-to-Peer-Verbindung mit WebRTC an den Benutzer übermittelt.2. Niedriglatenz-Mediastream
Beim Lesen über Stadia stoße ich in einigen Artikeln häufig auf den Hinweis auf WebRTC. Ich habe erkannt, dass WebRTC eine herausragende Technologie ist und sich hervorragend für den Einsatz in Cloud-Gaming eignet. WebRTC ist ein Projekt, das Echtzeitkommunikation für Webbrowser und mobile Anwendungen über eine einfache API bereitstellt. Es bietet eine peer-to-peer Verbindung, die für Medien optimiert ist, und hat integrierte Standardcodecs wie VP8 und H264.Ich habe es vorgezogen, den Benutzern eine möglichst angenehme Arbeit zu ermöglichen, anstatt eine hohe Grafikqualität aufrechtzuerhalten. Im Algorithmus sind einige Verluste zulässig. Bei Google Stadia gibt es einen zusätzlichen Schritt zur Reduzierung der Bildgröße auf dem Server, und die Frames werden vor der Übertragung an die Peer-Knoten auf eine höhere Qualität skaliert.
3. Verteilte Infrastruktur mit geografischer Routing
Unabhängig davon, wie optimiert der Komprimierungsalgorithmus und der Codec sind, ist das Netzwerk nach wie vor ein entscheidender Faktor, der am meisten zur Latenz beiträgt. Die Architektur muss einen Mechanismus zur Verbindung des nächstgelegenen Servers zum Benutzer haben, um die Rundreisezeit (RTT) zu minimieren. Die Architektur muss einen Koordinator und mehrere Streaming-Server weltweit haben: West-USA, Ost-USA, Europa, Singapur, China. Alle Streaming-Server müssen vollständig isoliert sein. Das System kann seine Verteilung regulieren, wenn ein Server dem Netzwerk beitritt oder es verlässt. So ermöglicht das Hinzufügen zusätzlicher Server bei hohem Datenverkehr horizontale Skalierung.4. Browserkompatibilität
Cloud-Gaming kommt am besten zur Geltung, wenn es nur minimale Anforderungen an die Nutzer stellt. Das bedeutet, dass es im Browser gestartet werden kann. Browser helfen, das Spielerlebnis für die Nutzer so komfortabel wie möglich zu gestalten, indem sie die Installation von Software und Hardware vermeiden. Außerdem gewährleisten Browser die Plattformübergreifende Kompatibilität für mobile und Desktop-Versionen. Glücklicherweise wird WebRTC in verschiedenen Browsern hervorragend unterstützt.5. Klare Trennung von Benutzeroberfläche und Dienst
Ich betrachte den Cloud-Gaming-Dienst als Plattform. Jeder sollte die Möglichkeit haben, alles mit der Plattform zu verbinden. Momentan habe ich in den Cloud-Gaming-Dienst integriert, weil LibRetro eine ansprechende Benutzeroberfläche für Retro-Spiele-Emulatoren wie SNES, GBA und PS bietet.6. Räume für Mehrspieler, Crowd Play und Deep-Linking mit dem Spiel
CloudRetro unterstützt zahlreiche neue Spielmodi wie CrowdPlay und Online-Mehrspieler für Retro-Spiele. Wenn mehrere Nutzer denselben Deep-Link auf verschiedenen Computern öffnen, sehen sie dasselbe laufende Spiel und können sogar daran teilnehmen.Darüber hinaus werden die Spielstände in der Cloud gespeichert. Das ermöglicht den Nutzern, das Spiel jederzeit auf jedem anderen Gerät fortzusetzen.
7. Horizontale Skalierbarkeit
Wie jede SAAS-Lösung müssen Cloud-Gaming-Dienste so entworfen werden, dass sie horizontal skalierbar sind. Die Struktur "Koordinator-Arbeiter" ermöglicht es, weitere Arbeiter hinzuzufügen, um einen größeren Datenverkehr zu bedienen.8. Keine Bindung an eine einzige Cloud
Die Infrastruktur von CloudRetro wird bei verschiedenen Cloud-Anbietern (Digital Ocean, Alibaba, benutzerdefinierter Anbieter) für verschiedene Regionen gehostet. Ich aktiviere den Start in einem Docker-Container für die Infrastruktur und konfiguriere die Netzwerkeinstellungen mit einem Bash-Skript, um eine Abhängigkeit von einem einzelnen Cloud-Anbieter zu vermeiden. Durch die Kombination mit NAT Traversal in WebRTC erhalten wir die Flexibilität, CloudRetro auf jeder Cloud-Plattform und sogar auf den Maschinen der Nutzer bereitzustellen.Architektonisches Design
Arbeiter: (oder der oben erwähnte Streaming-Server) vervielfacht Spiele, startet eine Codierungs-Pipeline und überträgt das codierte Medium an die Benutzer. Die Worker-Instanzen sind weltweit verteilt, und jeder Worker kann mehrere Benutzersitzungen gleichzeitig verarbeiten.
Koordinator: verantwortlich für die Zuordnung eines neuen Benutzers zu dem am besten geeigneten Worker für das Streaming. Der Koordinator kommuniziert über WebSocket mit den Workern.
Speicher für Spielzustände: zentraler Remote-Speicher für alle Spielstände. Dieses Speicher bietet wichtige Funktionen wie das Remote-Speichern/Laden.

Übergeordnete Architektur von CloudRetroBenutzerszenario
Wenn ein neuer Benutzer CloudRetro öffnet, wird in den Schritten 1 und 2, die in der Abbildung unten dargestellt sind, der Koordinator zusammen mit der Liste der verfügbaren Worker zur ersten Seite angefragt. Anschließend berechnet der Client in Schritt 3 die Latenzen für alle Kandidaten durch einen HTTP-Ping-Anruf. Diese Liste von Latenzen wird dann an den Koordinator zurückgesendet, damit er den am besten geeigneten Worker zur Betreuung des Benutzers bestimmen kann. In Schritt 4 wird ein Spiel erstellt. Zwischen dem Benutzer und dem zugewiesenen Worker wird eine WebRTC-Streaming-Verbindung hergestellt.

Benutzerszenario nach dem ZugriffWas sich im Worker befindet
Die Spiel- und Streaming-Pipelines werden innerhalb des Workers isoliert gespeichert und kommunizieren dort über eine Schnittstelle. Derzeit erfolgt diese Kommunikation durch den Datenaustausch im Speicher über im selben Prozess. Das nächste Ziel ist die Segregation, d.h. die unabhängige Ausführung des Spiels in einem anderen Prozess.

Interaktion der Komponenten des WorkersHauptbestandteile:
- WebRTC: Client-Komponente, die Benutzereingaben annimmt und das codierte Medium vom Server ausgibt.
- Spieleemulator: Spielkomponente. Dank der Libretro-Bibliothek kann das System das Spiel innerhalb desselben Prozesses ausführen und intern Medien und Eingangsströme abfangen.
- Ingame-Fotos werden erfasst und an den Encoder gesendet.
- Bild-/Audio-Encoder: Codierungs-Pipeline, die Medienbilder annimmt, diese im Hintergrund codiert und die codierten Bilder/Audios ausgibt.
Implementierung
CloudRetro setzt auf WebRTC als Schlüsseltechnologie. Bevor ich jedoch in die Einzelheiten der Implementierung in Golang eintauche, möchte ich über WebRTC selbst sprechen. Es ist eine erstaunliche Technologie, die mir geholfen hat, die Latenz bei der Datenübertragung auf weniger als eine Sekunde zu reduzieren.
WebRTC
WebRTC ist darauf ausgelegt, qualitativ hochwertige Peer-to-Peer-Verbindungen auf nativen mobilen Anwendungen und in Browsern durch einfache APIs bereitzustellen.
NAT Traversal
WebRTC ist bekannt für seine Funktionalität zur Umgehung von NAT. WebRTC ist für die Peer-to-Peer-Kommunikation konzipiert. Ziel ist es, die am besten geeignete direkte Verbindung zu finden und NAT-Gateways sowie Firewalls durch einen Prozess mit dem Namen zu umgehen. . Im Rahmen dieses Prozesses ermitteln die WebRTC-APIs Ihre öffentliche IP-Adresse mithilfe von STUN-Servern und leiten sie an einen Relay-Server weiter (), wenn keine direkte Verbindung hergestellt werden kann.
CloudRetro nutzt diese Möglichkeit jedoch nicht vollständig. Seine Peer-to-Peer-Verbindungen bestehen nicht zwischen Benutzern, sondern zwischen Benutzern und Cloud-Servern. Die Serverseite des Modells hat weniger Einschränkungen hinsichtlich der direkten Kommunikation als ein typisches Benutzergerät. Dies ermöglicht das Vorausöffnen von eingehenden Ports oder die direkte Nutzung öffentlicher IP-Adressen, da der Server nicht hinter einem NAT steht.
Früher wollte ich das Projekt in eine Plattform zur Bereitstellung von Spielen für Cloud Gaming verwandeln. Die Idee war, es Spieleschöpfern zu ermöglichen, Spiele und Streaming-Ressourcen anzubieten. Die Benutzer würden direkt mit den Anbietern interagieren. Auf diese dezentralisierte Weise fungiert CloudRetro nur als Medium, um externe Streaming-Ressourcen mit Benutzern zu verbinden, was es skalierbarer macht, wenn das Hosting nicht mehr daran hängt. Die Rolle von WebRTC NAT Traversal ist hier entscheidend, um die Initiierung der Peer-to-Peer-Verbindung zu externen Streaming-Ressourcen zu erleichtern, was die Verbindung des Schöpfers mit dem Netzwerk vereinfacht.
Videokompression
Die Videokompression ist ein unverzichtbarer Bestandteil der Pipeline, der maßgeblich zur Stabilität des Streams beiträgt. Obwohl es nicht unbedingt erforderlich ist, alle Details zur Videokodierung in VP8/H264 zu kennen, hilft das Verständnis des Konzepts dabei, die Parameter der Streaming-Video-Bitrate zu verstehen, unerwartetes Verhalten zu debuggen und die Latenz zu optimieren.
Die Videokompression für Streaming-Dienste ist eine komplexe Aufgabe, da der Algorithmus sicherstellen muss, dass die Gesamtdauer der Kodierung + die Übertragungszeit im Netzwerk + die Dekodierungszeit so gering wie möglich ist. Außerdem muss der Kodierungsprozess konsistent und kontinuierlich sein. Einige gegenseitige Kompromisse bei der Kodierung sind nicht anwendbar – beispielsweise können wir nicht eine längere Kodierzeit einem kleineren Dateiformat und einer kürzeren Dekodierungszeit vorziehen oder inkonsistente Kompression verwenden.
Die Idee der Videokompression besteht darin, unnötige Informationsbits auszuschließen, während ein akzeptables Genauigkeitsniveau für die Benutzer beibehalten wird. Neben der Kodierung einzelner statischer Bildrahmen zieht der Algorithmus seine Schlussfolgerungen für den aktuellen Frame aus dem vorherigen und dem nächsten, sodass nur deren Differenz übertragen wird. Wie im Beispiel mit Pacman zu sehen ist, werden nur die Differenzpunkte übertragen.

Vergleich von Videorahmen am Beispiel von PacmanAudiokompression
Analog dazu lässt der Audiokompressionsalgorithmus Daten weg, die vom Menschen nicht wahrgenommen werden können. Opus ist derzeit der leistungsfähigste Audiocodec. Er wurde entwickelt, um Audiowellen über ein Protokoll für geordnete Datagramme wie RTP (Real Time Transport Protocol) zu übertragen. Seine Latenz ist geringer als die von mp3 und aac, während die Qualität höher ist. Die Latenz beträgt normalerweise etwa 5~66,5 ms.
Pion, WebRTC in Golang
ist ein Open-Source-Projekt, das WebRTC nach Golang bringt. Anstatt die nativen C++-Bibliotheken von WebRTC zu umhüllen, ist Pion eine native Golang-Implementierung von WebRTC mit besserer Leistung, Integration in Go sowie Versionskontrolle für WebRTC-Protokolle.
Die Bibliothek bietet auch das Streaming von Daten mit einer Vielzahl ausgezeichneter integrierter Module mit einer Verzögerung von weniger als einer Sekunde. Sie hat ihre eigene Implementierung von STUN, DTLS, SCTP usw. und einige Experimente mit QUIC und WebAssembly. Diese Open-Source-Bibliothek ist eine wirklich gute Lernressource mit hervorragender Dokumentation, Implementierung von Netzwerkprotokollen und tollen Beispielen.
Die Pion-Community, angeführt von einem sehr leidenschaftlichen Ersteller, ist recht lebhaft und es gibt viele qualitativ hochwertige Diskussionen über WebRTC. Wenn Sie an dieser Technologie interessiert sind, schließen Sie sich an – Sie werden viel Neues erfahren.
CloudRetro in Golang schreiben

Implementierung eines Workers in GoGo-Kanäle in Aktion
Dank des eleganten Designs der Go-Kanäle werden Probleme im Bereich Ereignis-Streaming und Parallelismus erheblich vereinfacht. Wie in der Grafik arbeiten mehrere Komponenten parallel in verschiedenen GoRoutinen. Jede Komponente verwaltet ihren eigenen Zustand und kommuniziert über Kanäle. Die selektive Behauptung von Golang zwingt dazu, in jedem Moment des Spiels (game tick) ein atomarisches Ereignis zu verarbeiten. Das bedeutet, dass für dieses Design keine Sperrung erforderlich ist. Zum Beispiel, wenn der Benutzer speichert, wird ein vollständiger Snapshot des Spielzustands benötigt. Dieser Zustand muss kontinuierlich bleiben, während die Eingabe verarbeitet wird, bis das Speichern abgeschlossen ist. Während jedes game tick kann das Backend nur die Speicher- oder Eingabeoperation abwickeln, was den Prozess threadsicher macht.
func (e *gameEmulator) gameUpdate() { for { select { case <-e.saveOperation: e.saveGameState() case key := <-e.input: e.updateGameState(key) case <-e.done: e.close() return } } }Fan-in / Fan-out
Dieses Golang-Muster eignet sich hervorragend für mein Anwendungs-Szenario von CrowdPlay und Multiple Player. Indem alle Benutzereingaben in einem Raum in einen zentralen Eingabekanal integriert werden, werden die Spielmedien an alle Benutzer im Raum verteilt. Auf diese Weise erreichen wir eine Trennung des Spielzustands zwischen mehreren Spielsitzungen verschiedener Benutzer.

Synchronisation zwischen verschiedenen SitzungenNachteile von Golang
Golang ist nicht perfekt. Der Kanal ist langsam. Im Vergleich zu Blockierungen ist der Go-Kanal einfach eine einfachere Möglichkeit, parallele und streamende Ereignisse zu verarbeiten, bietet jedoch nicht die beste Leistung. Unter dem Kanal liegt eine komplexe Blockierungslogik. Daher habe ich einige Anpassungen an der Implementierung vorgenommen, indem ich Blockierungen und atomare Werte bei der Ersetzung von Kanälen zur Leistungsoptimierung erneut angewendet habe.
Darüber hinaus ist der Garbage Collector in Golang unmanaged, was manchmal verdächtig lange Pausen verursacht. Dies stört die Funktionalität von Echtzeitanwendungen erheblich.
CGO
Im Projekt wird eine bestehende VP8/H264 Golang-Bibliothek mit Open-Source-Code zur Medienkompression und Libretro für Spielemulatoren verwendet. All diese Bibliotheken sind einfach Wrapper um die C-Bibliothek in Go, die verwenden. . Einige der Nachteile sind in . Die Probleme, mit denen ich konfrontiert war, sind:
- die Unmöglichkeit, einen Crash in CGO zu erfassen, selbst mit Golang RecoveryCrash;
- die Unmöglichkeit, Engpässe in der Leistung zu identifizieren, wenn wir keine detaillierten Probleme in CGO erkennen können.
Fazit
Ich habe mein Ziel erreicht – ich habe mich in Cloud-Gaming-Dienste eingearbeitet und eine Plattform geschaffen, die es ermöglicht, nostalgische Retro-Spiele online mit meinen Freunden zu spielen. Die Erstellung dieses Projekts wäre ohne die Pion-Bibliothek und die Unterstützung der Pion-Community unmöglich gewesen. Ich bin äußerst dankbar für dessen intensive Entwicklung. Die einfachen APIs, die von WebRTC und Pion bereitgestellt werden, haben eine reibungslose Integration ermöglicht. Mein erstes Proof of Concept wurde in demselben Zeitraum veröffentlicht, obwohl ich im Voraus nichts über Peer-to-Peer-Verbindungen (P2P) wusste.
Trotz der Einfachheit der Integration ist P2P-Streaming tatsächlich ein sehr komplexes Gebiet der Informatik. Es muss sich mit der Komplexität jahrzehntelanger Netzwerkarchitekturen, wie IP und NAT, auseinandersetzen, um eine Peer-to-Peer-Session aufzubauen. Während meiner Arbeit an diesem Projekt habe ich viel wertvolles Wissen über Netzwerke und Leistungsoptimierung angehäuft, weshalb ich jedem empfehle, P2P-Produkte mit WebRTC auszuprobieren.
CloudRetro bedient alle Nutzungsszenarien, die ich als Retro-Gamer erwartet habe. Ich denke jedoch, dass es viele Bereiche im Projekt gibt, die ich verbessern kann, wie zum Beispiel die Netzwerkstabilität und -leistung, die Verbesserung der Grafikqualität der Spiele oder die Möglichkeit, Spiele zwischen Nutzern zu teilen. Ich arbeite hart daran. Bitte bleiben Sie dran. und unterstützen Sie es, wenn es Ihnen gefällt.
Quelle: habr.com







