Облачный гейминг в браузере
В облако сегодня перебирается всё больше категорий приложений, в том числе игры. У VK Play Cloud уже есть свои нативные приложения под Windows, Mac, Android и Android TV, которые позволяют играть в облаке в современные требовательные игры даже на слабых компьютерах. А теперь мы добавили ещё и веб-клиент — запускать можно прямо в браузере.
Меня зовут Александр, я работаю в команде клиентской разработки для игры в облаке — VK Play Cloud. Заваривайте чай, запасайтесь печеньками, и расскажу, как сделать облачный гейминг в браузере. Предупреждаю сразу, статья срубает технические верхушки ёлок и рассказывает «как это было», поехали.
Как работает наше облако
Для игры в облаке необходима клиентская часть, которая:
подключается к менеджер-серверу;
получает и воспроизводит видео- и аудиопотоки с сервера;
считывает данные с устройств ввода пользователя для передачи на сервер;
с помощью различных алгоритмов отправляет, обрабатывает и восстанавливает данные;
взаимодействует с пользователем с помощью графического интерфейса.
А вся нагрузка ложится на серверную часть, которая:
кодирует аудио- и видеоданные с помощью кодеков;
подготавливает данные с помощью различных алгоритмов для отправки клиентской части;
принимает и обрабатывает данные с устройств ввода.
Первый блин комом или первый прототип
Для нашей команды это было в новинку, поэтому первым делом решили запустить только поток видео. Провести ряд тестов, в том числе на пропускную способность и устойчивость к потерям, и сделать выводы. За основу взяли WebRTC технологию. Набросали план действий/задач на клиент и сервер.
Клиент
Для клиентской части на вебе выбрали связку react’а + typescript. Так как нативные клиенты написаны с использованием С99 и С++14, то появилась идея по-максимуму перенести код в веб-приложение. Проанализировав кодовую базу, мы выделили модули, которые возможно перенести — (аудио/видео/устройства ввода), а какие придется переписать на typescipt — (подключения к менеджеру и серверу).
Подключение к менеджеру реализовали с помощью websocket’ов и protobuf. А вот подключение к серверу, уже через WebRTC с помощью пирингового подключения и дата каналов. Теперь начальное подключение было готово, можно было приступать дальше — компиляция С/C++ в WebAssembly и оценка производительности.
Первым на очереди был модуль для работы с видео. Чтобы собрать модуль взяли инструмент Emscripten — он позволяет скомпилировать наш C/C++ код в модуль WebAssembly, который в дальнейшем может быть запущен в веб-браузере. Подготовили необходимые классы для Embind — в заголовочные файлы C++ было добавлено описания класса/enum’ов/структур для вызова из JavaScript.
Что-то похожее на это
#ifdef BUILD_EMSCRIPTEN
#include <emscripten.h>
#include <emscripten/bind.h>
EMSCRIPTEN_BINDINGS(em_video_class) {
emscripten::class_<video_class>("video_class")
.function("process_packet", emscripten::optional_override(
[](video_class& self, uintptr_t addr, uint32_t size, long long microseconds)
{
auto ptr = reinterpret_cast<uint8_t*>(addr);
auto us = std::chrono::microseconds(microseconds);
self.video_class::process_packet(ptr, size, us);
}))
.function("status", &video_class::status)
.function("frame_size", &video_class::frame_size)
.function("frame_data", emscripten::optional_override(
[](video_class& self, uint32_t id, uintptr_t addr, size_t size)
{
auto ptr = reinterpret_cast<uint8_t*>(addr);
self.video_class::frame_data(id, ptr, size);
}))
}
#endif
В конечном итоге Emscripten успешно интегрировался в наш Cmake и на выходе выдавал video_module.js. Загрузили модуль видео в отдельном потоке — Web Worker'е (привет многопоточность из мира C++). Полученные данные с помощью дата канала отдавали в модуль, и на выходе получали кадр.
Но радоваться было рано, у нас потихоньку утекала память при вызове C++ метода из JavaScript, как оказалось при создании std::vector’а...
Сервер
Со стороны сервера взяли библиотеку WebRTC на С++, скомпилировали, подключили и реализовали все необходимые интерфейсы. Это тоже была боль и о подключении WebRTC С++ библиотеки со стороны сервера есть множество интересных статей на хабре [webrtc server].
Реализованные интерфейсы
rtc::MessageHandler
webrtc::PeerConnectionObserver
webrtc::DataChannelObserver
webrtc::CreateSessionDescriptionObserver
webrtc::SetSessionDescriptionObserver
webrtc::VideoTrackSource
webrtc::FakeAudioDeviceModule
webrtc::Notifier<webrtc::AudioSourceInterface>
Ещё одна трудность была в том, что клиент и сервер не общаются напрямую, потому что серверная часть защищена сетевым экраном (firewall), а клиентская находится за какими-нибудь маршрутизаторами. И для пирингового подключения необходим посредник — STUN-сервер. С его реализацией пришлось разбираться и даже подключать админов. После всех разбирательств видео поток наконец-то заработал.
Так как у нас было мало опыта работы с WebRTC, мы выбрали неверный путь — передачу видео/аудио данных по data-каналам (SCTP протокол). В итоге пропускная способность нашего видеопотока не превышала 8-10 Мбит/сек, буферизовалась и мы получали почти все прелести TCP протокола. Нас это категорически не устраивало, и тогда мы решили перевести data-канал на протокол RTP. Но к тому времени его поддержку из WebRTC уже убрали.
Работа над ошибками
Data-каналы работают по протоколу SCTP, предлагающему встроенное управление потока передачи данных с контролем перегрузки. А в своих нативных клиентах мы брали обычный протокол UDP и накручивали туда свои разработки.Технология WebRTC содержит в себе возможность передачи видео/аудио через медиапотоки.
Пришлось выбросить почти всё, что мы компилировали с помощью Emscripten в WebAssembly — алгоритмы восстановления данных и регулировки битрейта, шифрование — остался только модуль конвертации курсора (он необходим для отрисовки курсора на клиенте), потому что браузеры не дают возможность работы с сырыми данными из медиапотоков. Data-потоки нам это позволяли: мы брали из них сырые данные и отправляли в свой скомпилированный код, который потом работал как в нативных приложениях. Также мы полностью убрали Web Worker'ы. WebRTC для инициализации видео, протоколов и различных алгоритмов использует SDP-диаграммы (протокол описания сеанса связи), поэтому нам пришлось ещё и с ними поработать: задать кодек H.264, профиль HIGH, стартовый/минимальный и максимальный битрейт, и оптимальные настройки встроенных алгоритмов восстановления данных.
В результате сервер кодирует картинку и с помощью медиапотока отправляет на клиент. Клиент принимает поток и внутри браузера его декодирует и показывает. Результаты прогона тестов нас устроили. Конечно не производительность, как на нативных приложениях, но пропускной способности видео оказалось достаточно для комфортной игры. Теперь уже можно было дописать всё необходимое для работы со звуком и устройствами ввода.
Общий список задач, которые мы решили при создании игры в облаке через браузер:
С клиентской стороны:
подключение и взаимодействие с менеджером-сервером;
отрисовка курсора;
снятие нажатий с устройств ввода пользователя — клавиатуры, мыши, джойстика;
работа в полноэкранного режиме браузера (ох уж эти браузеры с F11);
виртуальная клавиатура;
различные информационные сообщения и ошибки;
работа с буфером обмена;
локализация;
использование микрофона;
логирование;
сбор статистики.
С серверной стороны:
собрали и подключили библиотеку WebRTC;
отрефакторили код для использования интерфейсов WebRTC;
реализовали все необходимые интерфейсы библиотеки WebRTC.
Что получилось
Теперь у нас видео- и аудиоданные передаются в медиапотоках, а информация от устройств ввода, буфер обмена и текстура курсора через data-каналы. Мы ещё не всё перенесли на WebRTC — это Beta, и да, мы всё ещё боремся с Jitter Buffer.
Список поддерживаемых платформ и браузеров на данный момент:
Windows | MacOS | Linux | |
Chrome | + | + | в разработке |
Yandex | + | - | - |
Opera | + | - | - |
Edge | + | - | - |
Safari | - | в разработке | - |
Firefox | - | - | - |
А если вы поклонник нативных клиентов, то можете скачать наше приложение под Windows / MacOS / Android / Android TV. Для тех у кого нету Google Play можно скачать apk напрямую VK-Play-Cloud.apk / VK-Play-Cloud-TV.apk
Помимо обширного каталога игр вы также можете запустить «Виртуальный компьютер» и делать всё, что захотите прямо в браузере cloud.vkplay.ru. Попробуйте облачный гейминг со скидкой 20% по промокоду FOR_WEBRTC_HABR (промокод действителен до 16.02.2023).