Software as a Service, Infrastructure as a Service, Platform as a Service, Communication Platform as a Service, Video Conferencing as a Service, but what about Cloud Gaming as a Service? Several attempts have already been made to create Cloud Gaming, such as Stadia, recently launched by Google. , but can others use WebRTC in the same way?
Thanh Nguyen decided to explore this possibility in his open-source project CloudRetro. CloudRetro is based on Pion, WebRTC library in Go (thanks to from the Pion development team for helping with this article). In this article, Thanh reviews the architecture of his project and shares what insights he gained and the challenges he faced during development.
Introducere
Last year, when Google announced Stadia, I was blown away. The idea is so unique and innovative that I kept wondering how it was even possible with existing technologies. My desire to understand this topic better led me to create my version of an open-source cloud game. The result was simply fantastic. Below, I would like to share the process of working on my year-long .
TLDR: a brief slide version with the main points
Why Cloud Gaming is the Future
I believe that Cloud Gaming will soon represent a new generation not only in gaming but also in other areas of IT. Cloud gaming is the pinnacle of the client/server model. This model maximizes backend management and minimizes frontend workload by placing game logic on a remote server and streaming images/audio to the client. The server handles heavy processing, so the client is no longer dependent on hardware limitations.
Google Stadia essentially allows you to play (i.e., high-end blockbuster games) on an interface similar to YouTube. The same methodology can be applied to other heavy offline applications, such as operating systems or 2D/3D graphic design, so we could reliably run them on low-spec devices across different platforms.

The future of this technology: imagine if Microsoft Windows 10 worked in the Chrome browser?
Cloud gaming is technically complex
Gaming is one of the rare fields where a constant quick response from the user is required. If occasionally there is a delay of 2 seconds when clicking on a page, it's acceptable. Live video streams typically lag a few seconds but still provide sufficient usability. However, if a game often lags by 500 ms, playing becomes impossible. Our goal is to achieve an extremely low latency so that the gap between input and media is as minimal as possible. Therefore, the traditional approach to streaming video is not applicable here.

The general template for cloud gaming
The open-source project CloudRetro
I decided to create a test sample of cloud gaming to check if all this is possible under such strict network limitations. For the proof of concept, I chose Golang as it is the most familiar language to me and well-suited for this implementation for many other reasons, as I found out later. Go is simple and develops very quickly; channels in Go are great for managing multithreading.
Proiect – an open-source cloud gaming service for retro gaming. The goal of the project is to bring the most comfortable gaming experience to traditional retro games and add multiplayer.
You can find more details about the project here: .
CloudRetro functionality
To demonstrate the full power of cloud gaming, CloudRetro uses retro games. This allows for a multitude of unique gaming experiences.
- Game portability
- Instant playback upon opening the page; no downloads or installations required
- Works in mobile browsers, so no software installation is needed to start
- Gaming sessions can be shared across multiple devices and stored in the cloud for the next login
- The game can be streamed, or multiple users can play it directly:
- Crowdplay like TwitchPlayPokemon, but more cross-platform and real-time
- Offline games online. Many users can play without network configuration. In Samurai Shodown, 2 players can now play over the CloudRetro network

A demo version of multiplayer online games on different devicesInfrastructură
Requirements and technology stack
Below is a list of requirements that I set before starting the project.
1. Un jucător
Această cerință poate părea nu foarte importantă și evidentă aici, dar este una dintre concluziile mele esențiale; permite jocurilor cloud să se mențină cât mai departe de serviciile tradiționale de streaming. Dacă ne concentrăm pe jocul solo, putem scăpa de un server centralizat sau CDN, deoarece nu avem nevoie să transmitem informații în masă. În loc să încărcăm fluxuri pe un server care consumă resurse sau să trimitem pachete către un server WebSocket centralizat, fluxurile de servicii sunt transmise utilizatorului direct printr-o conexiune peer-to-peer WebRTC.2. Flux media cu întârziere mică
Citind despre Stadia, întâlnesc adesea în unele articole mențiuni despre WebRTC. Am realizat că WebRTC este o tehnologie remarcabilă, perfectă pentru utilizarea în jocurile cloud. WebRTC este un proiect care oferă browserelor web și aplicațiilor mobile comunicare în timp real printr-o API simplă. Acesta asigură o conexiune peer-to-peer, optimizată pentru media și vine cu codec-uri standard încorporate, cum ar fi VP8 și H264.Am preferat asigurarea unei experiențe de utilizare cât mai confortabile, în locul menținerii unei calități ridicate a graficii. În algoritm sunt permise unele pierderi. Google Stadia are un pas suplimentar pentru reducerea dimensiunii imaginii pe server, iar cadrele sunt scalate la o calitate mai înaltă înainte de a fi transmise nodurilor peer-to-peer.
3. Infrastructură distribuită cu rutare geografică
Indiferent cât de optimizat este algoritmul de compresie și codul, rețeaua rămâne un factor decisiv care contribuie cel mai mult la latență. Arhitectura trebuie să aibă un mecanism de asociere a celui mai apropiat server utilizatorului pentru a reduce timpul de răspuns (RTT). Arhitectura trebuie să aibă un coordonator și mai multe servere de streaming distribuite în întreaga lume: Vestul SUA, Estul SUA, Europa, Singapore, China. Toate serverele de streaming trebuie să fie complet izolate. Sistemul poate ajusta distribuția sa atunci când un server se alătură sau părăsește rețeaua. Astfel, în cazul unui trafic mare, adăugarea de servere suplimentare permite scalarea orizontală.4. Compatibilitate cu browserul
Jocurile cloud se prezintă cel mai bine atunci când necesită un minim de efort din partea utilizatorilor. Aceasta înseamnă că există posibilitatea de a porni direct în browser. Browserele ajută la realizarea unei experiențe de joc cât mai confortabile pentru utilizatori, scutindu-i de instalarea de software și hardware. De asemenea, browserele contribuie la asigurarea compatibilității între platforme pentru versiunile mobile și desktop. Din fericire, WebRTC este excelent suportat în diverse browsere.5. Împărțirea clară a interfeței de joc și a serviciului
Privesc serviciul de jocuri cloud ca pe o platformă. Fiecare ar trebui să aibă posibilitatea de a conecta orice la platformă. Acum am integrat cu serviciul de jocuri cloud, deoarece LibRetro oferă o interfață frumoasă de emulator de jocuri pentru jocuri retro, cum ar fi SNES, GBA, PS.6. Camere pentru multiplayer, crowd play și linking extern (deep-link) cu jocul
CloudRetro suportă numeroase noi modalități de joc, cum ar fi CrowdPlay și Multiplayer Online pentru jocuri retro. Dacă mai mulți utilizatori deschid același deep-link pe computere diferite, ei vor vedea aceeași sesiune de joc rulată și pot chiar să se alăture acesteia.Mai mult, stările jocului sunt stocate în cloud. Aceasta permite utilizatorilor să continue jocul în orice moment pe orice alt dispozitiv.
7. Scalare orizontală
Ca orice serviciu SAAS în prezent, jocurile cloud trebuie să fie concepute pentru a fi scalabile orizontal. Proiectarea „coordonator-muncitor” permite adăugarea de muncitori suplimentari pentru a gestiona un trafic mai mare.8. Fără dependență de un singur cloud
Infrastructura CloudRetro este găzduită pe diferite furnizori de cloud (Digital Ocean, Alibaba, furnizor personalizat) pentru diferite regiuni. Activez pornirea în containere Docker pentru infrastructură și configurez parametrii rețelei folosind un script bash, pentru a evita dependența de un singur furnizor de cloud. Combinând acest lucru cu NAT Traversal în WebRTC, putem obține flexibilitate pentru desfășurarea CloudRetro pe orice platformă cloud și chiar pe mașinile oricărui utilizator.Design arhitectural
Muncitor: (sau serverul de streaming menționat anterior) multiplică jocurile, inițiază procesul de codificare și transmite media codificată utilizatorilor. Instanțele worker sunt distribuite în întreaga lume, iar fiecare worker poate gestiona mai multe sesiuni ale utilizatorilor simultan.
Coordonator: responsabil cu asocierea unui nou utilizator cu cel mai potrivit worker pentru streaming. Coordonatorul interacționează cu workerele prin WebSocket.
Stocarea stărilor jocurilor: un depozit centralizat pentru toate stările jocului. Acest depozit oferă funcții importante, cum ar fi salvarea/încărcarea de la distanță.

Arhitectura de nivel înalt pentru CloudRetroScenariul utilizatorului
Când un utilizator nou deschide CloudRetro în pașii 1 și 2, ilustrați mai jos, coordonatorul împreună cu lista workernelor disponibile este solicitat pe prima pagină. După aceea, la pasul 3, clientul calculează întârzierile pentru toți candidații printr-un ping HTTP. Această listă de întârzieri este apoi trimisă înapoi coordonatorului, astfel încât acesta să poată determina cel mai potrivit worker pentru a deservi utilizatorul. La pasul 4, mai jos, se creează jocul. Se stabilește o conexiune de streaming WebRTC între utilizator și worker-ul desemnat.

Scenariul utilizatorului după obținerea accesuluiCe este în interiorul worker-ului
Fluxurile de joc și streaming sunt stocate izolat în interiorul worker-ului și își schimbă informații printr-o interfață. În prezent, această comunicare se realizează prin transmiterea de date în memorie prin în același proces. Următoarea țintă este segregarea, adică rularea independentă a jocului într-un alt proces.

Interacțiunea componentelor worker-uluiComponente de bază:
- WebRTC: componenta clientului, care acceptă intrarea utilizatorului și expune media codificată de pe server.
- Emulatorul de jocuri: componentul de joc. Datorită bibliotecii Libretro, sistemul este capabil să ruleze jocul în interiorul aceluiași proces și să intercepteze intern media și fluxul de intrare.
- Cadrele din joc sunt capturate și trimise encoder-ului.
- Encoder de imagine/audio: procesul de codificare care primește cadrele media, le codifică în fundal și produce imagini/audio codificate.
Implementarea
CloudRetro se bazează pe WebRTC ca tehnologie de bază, așa că înainte de a aprofunda detalii despre implementarea în Golang, am decis să vorbesc despre WebRTC în sine. Aceasta este o tehnologie uimitoare, care m-a ajutat enorm să ating o întârziere în transmisia datelor de doar câteva fracțiuni de secundă.
WebRTC
WebRTC este proiectat pentru a oferi conexiuni peer-to-peer de înaltă calitate pe aplicații mobile native și în browsere prin intermediul unor API-uri simple.
Traversare NAT
WebRTC este cunoscut pentru funcționalitatea sa de NAT Traversal. WebRTC este destinat comunicării peer-to-peer. Scopul său este să găsească cea mai potrivită cale directă, evitând gateway-urile NAT și firewalls pentru comunicare peer-to-peer printr-un proces numit . În cadrul acestui proces, API-urile WebRTC își găsesc adresa IP publică prin servere STUN și o redirecționează către un server de retransmisie (), atunci când nu se poate stabili o conexiune directă.
Cu toate acestea, CloudRetro nu folosește complet această capacitate. Conexiunile sale peer-to-peer nu existǎ între utilizatori, ci între utilizatori și servere cloud. Partea serverului modelului are mai puține restricții privind comunicația directă decât un dispozitiv obişnuit utilizator. Acest lucru permite deschiderea prealabilă a porturilor de intrare sau utilizarea adreselor IP publice direct, deoarece serverul nu se află în spatele unui NAT.
În trecut, am dorit să transform proiectul într-o platformă de distribuție a jocurilor pentru Cloud Gaming. Ideea era de a permite creatorilor de jocuri să ofere jocuri și resurse de streaming. Iar utilizatorii ar interacționa direct cu furnizorii. Într-un mod descentralizat, CloudRetro este doar un mediu pentru conectarea resurselor externe de streaming la utilizatori, ceea ce îl face mai scalabil, deoarece nu se mai ocupă de hosting. Rolul NAT Traversal al WebRTC este foarte important pentru facilitarea inițierii conexiunii peer-to-peer pe resurse externe de streaming, ceea ce simplifică conectarea creatorului la rețea.
compresia video
Compresia video este o parte esențială a fluxului de lucru, care contribuie semnificativ la fluiditatea stream-ului. Deși nu este necesar să cunoașteți toate detaliile de codare video în VP8/H264, înțelegerea conceptului ajută la gestionarea parametrilor de viteză ai video-ului streaming, la depanarea comportamentului neașteptat și la ajustarea întârzierii.
Compresia video pentru servicii de streaming reprezintă o provocare complexă, deoarece algoritmul trebuie să garanteze că timpul total de codare + timpul de transmisie pe rețea + timpul de decodare este cât mai mic posibil. În plus, procesul de codare trebuie să fie consecvent și continuu. Unele compromisuri în codare nu se aplică – de exemplu, nu putem prefera un timp de codare mai lung pentru un fișier mai mic și un timp de decodare mai mic sau să folosim compresie neregulată.
Ideea compresiei video constă în eliminarea bitilor inutili de informație, păstrând în același timp un nivel acceptabil de exactitate pentru utilizatori. Pe lângă codarea cadrelor statice individuale ale imaginii, algoritmul deduce pentru cadrul curent din cadrelor anterioare și următoare, așa că se trimite doar diferența dintre acestea. Așa cum se poate observa în exemplul cu Pacman, se transmit doar punctele diferențiale.

Comparația cadrelor video în exemplul PacmanCompresia audio
În mod similar, algoritmul de compresie a sunetului omite datele care nu pot fi percepute de oameni. Opus este în prezent codec-ul audio cu cea mai bună performanță. Acesta este dezvoltat pentru a transmite unda audio printr-un protocol de datagrame ordonate, cum ar fi RTP (Real Time Transport Protocol – protocol de transport de date în timp real). Întârzierea sa este mai mică decât cea a mp3 și aac, iar calitatea este superioară. Întârzierea este în general de aproximativ 5~66,5 ms.
Pion, WebRTC în Golang
– este un proiect cu sursă deschisă care îmbunătățește WebRTC pe Golang. În loc de a învârti biblioteci native C++ WebRTC, Pion este o implementare nativă Golang a WebRTC cu performanțe superioare, integrare cu Go și control al versiunilor pe protocoalele WebRTC.
Biblioteca oferă, de asemenea, streaming de date cu numeroase module excelente încorporate, cu o întârziere de mai puțin de o secundă. Are propriile sale implementări STUN, DTLS, SCTP etc. și unele experimente cu QUIC și WebAssembly. Această bibliotecă open-source este cu adevărat o sursă bună de învățare, având o documentație excelentă, implementări de protocoale de rețea și exemple interesante.
Comunitatea Pion, condusă de un creator foarte pasionat, este destul de activă, având multe discuții de calitate despre WebRTC. Dacă sunteți interesat de această tehnologie, alăturați-vă – veți învăța multe lucruri noi.
Scrierea CloudRetro în Golang

Implementarea unui worker în GoCanalele Go în acțiune
Datorită designului elegant al canalelor Go, problemele legate de streaming-ul evenimentelor și paralelism sunt semnificativ simplificate. Așa cum se vede în diagramă, mai multe componente funcționează în paralel în diferite GoRoutines. Fiecare componentă își gestionează propriul stare și comunică prin canale. Afirmarea selectivă în Golang determină procesarea unui singur eveniment atomic la fiecare moment în joc (game tick). Acest lucru înseamnă că pentru un astfel de design nu este nevoie de blocare. De exemplu, atunci când un utilizator salvează, este necesar un snapshot complet al stării jocului. Această stare trebuie să rămână consecventă, executând intrări până când salvarea este completă. În timpul fiecărui game tick, backend-ul poate procesa doar operația de salvare sau de intrare, făcând procesul sigur pentru multithreading.
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
Acest șablon Golang se potrivește perfect pentru cazul meu de utilizare CrowdPlay și Multiple Player. Urmând acest șablon, toate intrările utilizatorilor dintr-o cameră sunt integrate într-un canal de intrare central. Media de joc este apoi distribuită tuturor utilizatorilor dintr-o cameră. Astfel, obținem separarea stării jocului între mai multe sesiuni de joc ale diferitelor utilizatori.

Sincronizarea între sesiuni diferiteDezavantajele Golang
Golang nu este perfect. Canalul este lent. Comparativ cu blocajul, canalul Go este pur și simplu o modalitate mai simplă de a gestiona evenimentele paralele și de flux, dar canalul nu oferă cea mai bună performanță. Sub canal există o logică complexă de blocare. Prin urmare, am făcut unele ajustări în implementare, reutilizând blocările și valorile atomice atunci când am înlocuit canalele pentru a optimiza performanța.
În plus, garbage collector-ul din Golang este necontrolat, ceea ce poate duce la pauze suspect de lungi. Acest lucru interferează semnificativ cu funcționarea aplicațiilor de streaming în timp real.
CGO
În proiect este utilizată o bibliotecă existentă VP8/H264 Golang cu sursă deschisă pentru comprimarea mediei și Libretro pentru emulatorii de jocuri. Toate aceste biblioteci sunt pur și simplu wrapper-uri ale bibliotecii C în Go utilizând . Unele dintre dezavantaje sunt enumerate în . Problemele cu care m-am confruntat sunt:
- imposibilitatea de a captura crash-uri în CGO, chiar și cu ajutorul Golang RecoveryCrash;
- imposibilitatea de a identifica un punct limită în performanță, atunci când nu putem detecta problemele detaliate în CGO.
Concluzie
Am atins obiectivul meu – am înțeles serviciile de jocuri în cloud și am creat o platformă care ajută la jocul retro cu prietenii mei online. Crearea acestui proiect nu ar fi fost posibilă fără biblioteca Pion și suportul comunității Pion. Sunt extrem de recunoscător pentru dezvoltarea sa intensă. API-urile simple oferite de WebRTC și Pion au asigurat o integrare lină. Prima mea dovadă a conceptului a fost lansată în aceeași săptămână, deși nu știam anterior despre conectivitatea peer-to-peer (P2P).
În ciuda simplității integrării, streamingul P2P este într-adevăr un domeniu foarte complex în știința computerelor. Trebuie să se confrunte cu complexitatea arhitecturilor de rețea îndelungate, cum ar fi IP și NAT, pentru a crea o sesiune peer-to-peer. Pe parcursul lucrului la acest proiect, am acumulat multe cunoștințe valoroase despre rețea și optimizarea performanței, așa că recomand tuturor să încerce să construiască produse P2P folosind WebRTC.
CloudRetro satisface toate scenariile de utilizare pe care le așteptam, din perspectiva mea de retro-gamer. Totuși, cred că există multe domenii în proiect pe care le pot îmbunătăți, cum ar fi creșterea fiabilității și performanței rețelei, asigurarea unei calități mai bune a graficii jocurilor sau posibilitatea de a partaja jocuri între utilizatori. Lucrez din greu la asta. Vă rog să urmăriți și să-l susțineți dacă vă place.
Sursa: habr.com







