
Привет, Хабр! На связи Женя Фирстов, лид команды разработки бэкенда видеозвонков в Т-Банке. Расскажу про устройство современных систем Есть пример известного случая, когда само наличие видеозвонка в Т-Банке спасло деньги клиента.
Как работает передача видео по сети
В основе большинства современных ВКС лежит WebRTC — протокол передачи медиа по сети, который Google выпустил в opensource в 2011 году. WebRTC поддерживают все популярные браузеры, есть SDK для Android и iOS. Существуют и другие протоколы — например, в Zoom написали свой.
Когда нет ресурсов на разработку своего протокола, единственный доступный вариант — это WebRTC.
Предположим, есть два устройства в сети и нужно в режиме реального времени вывести видео с камеры одного устройства на экран другого. Из-за необходимости передавать трафик 
После обмена адресами для приема трафика можно передавать видео и аудио. Качество сети при real-time-коммуникации оказывает решающее влияние на воспринимаемое качество картинки и звука, поэтому в WebRTC встроен набор механизмов для компенсации сетевых проблем.
Первая сетевая проблема — задержки при передаче данных. В WebRTC для их измерения используют обычно RTT — round trip time. Вторая проблема — packet loss, потеря пакетов, измеряется в процентах.
Во время звонка обе проблемы накладываются друг на друга и приводят к эффекту jitter: равномерно отправляемые из источника пакеты приходят на принимающую сторону с неравномерной задержкой и нарушением порядка. Для компенсации jitter используется jitter buffer: пакеты некоторое время перед воспроизведением накапливаются в памяти, дожидаясь отстающих.
С packet loss тоже можно бороться: есть подход избыточного кодирования 
Свои компромиссы есть и в мире кодеков (алгоритмов преобразования медиапотока в байты и обратно). С одной стороны, можно как следует прогреть процессор и получить отлично сжатое видео, которое можно без проблем передать даже по ограниченному каналу связи, с другой — можно кодировать меньше и из-за этого генерировать больший битрейт. Например, современные видеокодеки VP9 и AV1 сжимают видео лучше, но тратят на это больше ресурса процессора, чем более классический H264. Также нужно держать в голове, что не у всех пользователей будет поддерживаться нужный вам кодек и что избыточная нагрузка на CPU может привести к фризам на клиенте. Глубже погрузиться в трейдоффы между кодеками поможет статья на MDN.
Серверные топологии
WebRTC дает разработчику все необходимое для передачи медиапотока в режиме «точка-точка», но не предлагает какого-то единого механизма для построения больших видеоконференций. Если в звонке больше двух участников, есть несколько вариантов топологий:
-
Mesh, в которой каждый участник напрямую связан с каждым другим. Единственный ее плюс — низкая задержка, главный очевидный минус — линейный рост передаваемого и принимаемого трафика на клиенте в зависимости от числа участников, из-за чего на практике качество звонка деградирует где-то в районе подключения 8-го пользователя. Использование этой топологии ограничено обычно звонками один на один.
-
MCU, в которой каждый участник отправляет свои потоки на сервер, где происходит микширование аудиопотоков всех участников в один и объединение всех видеопотоков в «сеточку», благодаря чему каждый участник получает один аудиопоток и один видеопоток.
Трейдофф здесь простой: за счет интенсивной нагрузки на серверный CPU все участники звонка экономят трафик и процессорное время. Помним, что каждый поток нужно не только принять, но еще и декодировать ☝️
-
SFU, где сервер занимается пересылкой потоков: участник отправляет свои потоки на сервер и подписывается на получение нужных ему потоков других участников.
На практике больше возможностей для горизонтального масштабирования дает симбиоз SFU и MCU: SFU для видео, MCU для аудио. Из определения SFU следует принципиальная приспособленность этой топологии для горизонтального масштабирования: если все потоки в рамках звонка не помещаются на один сервер, то часть участников начинает публиковать свои потоки на соседний, при этом с точки зрения получения потоков на клиенте ничего не меняется. Клиент так же запрашивает видео нужных ему участников, просто часть из них приходит к нему с другого сервера.
SFU можно использовать и для аудио, но здесь разработчики ВКС обычно оптимизируют трафик на клиенте. Микширование аудио на сервере — относительно легкий процесс, он не требует таких колоссальных ресурсов CPU, как микширование видео (из-за чего, кстати, MCU для видео не получило широкого распространения).
Из-за того, что каждый квадратик в видеоконференции — это отдельный видеопоток, разработчики ВКС всячески хитрят: ограничивают максимальное отображаемое в интерфейсе количество участников с включенной камерой, и чем больше показывается
участников, тем хуже будет получаемое с сервера качество видео. Также на руку играет то, что сильно много квадратиков с видео обычно просто не помещается на экран.
К слову, о качестве видео: чтобы на клиенте в зависимости от текущего состояния интерфейса можно было динамически переключаться между хорошим и плохим качеством видео другого участника, сервер обычно предоставляет возможность получать один и тот же поток в разных качествах. Здесь есть три основных подхода к реализации: simulcast, при котором каждый пользователь отправляет на сервер видео сразу же в нескольких качествах в отдельных потоках; SVC (scalable video coding), где видео кодируется в нескольких качествах в рамках одного потока; подход с серверным транскодированием, когда клиент передает на сервер видео в максимальном доступном качестве, дальше это видео преобразуется в несколько потоков с разным качеством. Трейдофф здесь понятен: накладные расходы на кодирование видео сразу в несколько разрешений будет нести либо клиент (в случае с simulcast и SVC), либо сервер.
Демонстрация экрана
Закодить передачу видео с экрана через WebRTC просто. Нужно получить из API браузера или операционки поток с экрана и подсунуть его в WebRTC-подключение как еще один видеопоток. Для других участников, равно как и для медиасервера, это будет еще один видеопоток, неотличимый по свойствам от видео с камеры. Звучит просто. Вот только работать такая схема будет не очень.
Если вы будете передавать трансляцию экрана просто как еще одно видео, то картинка получится мыльной, а мелкий шрифт на демонстрируемой странице и вовсе может стать нечитаемым. При демонстрации экрана нам важно сохранить качество картинки. К счастью, иметь высокий FPS при этом хоть и желательно, но не обязательно, ведь картинка на экране преимущественно статична. Поэтому все решения по качественной трансляции экрана основаны на этом трейдоффе: качество видео повышается за счет снижения FPS. В базовом виде это можно реализовать инструментами WebRTC, с использованием так называемого API data channels (видео передается через SCTP), но гиганты индустрии обычно идут еще дальше и в дополнение к data channels реализуют кастомное кодирование потока.
Виртуальные фоны
История виртуальных фонов повторяет историю демонстрации экрана: сделать, чтобы работало, будет легко, но для доведения качества до приемлемого уровня придется постараться.
Процесс генерации виртуального фона состоит из двух этапов:
-
На кадре из видеопотока выделить силуэт.
-
Полученный силуэт нарисовать на другом фоне.
Самая простая реализация этого алгоритма в браузере на клиенте выглядит примерно так:
-
Разбить исходный поток с камеры на кадры.
-
Взять готовую нейросеть для выделения силуэтов, например Google MediaPipe, и выделять через нее силуэт на каждом кадре.
-
Рисовать силуэт поверх фона, используя canvas.
-
Соединить полученные кадры в новый поток, который передается на сервер.
Кодится это быстро, у меня написание и отладка заняли пару часов. Нюанс здесь заключается в низкой производительности решения. Дело в том, что для стандартного видео с частотой 25 FPS у нас есть всего 40 ms на обработку одного кадра. Новенькому макбуку разработчика этого будет достаточно, но вот бюджетный смартфон может и не вытянуть.
Помним, что в видеоконференции у нас и так параллельно кодируются и декодируются несколько дорожек, а тут еще запускается нейросеть — и приложение начинает гонять кадры с видеокарты в память и обратно.
Так почему бы не унести всю вычислительную нагрузку на сервер? Справедливый вопрос, но почему-то никто из известных мне распространенных ВКС так не сделал. Может, жалко серверных мощностей или возникают еще какие-то неочевидные проблемы.
Вместо переноса нагрузки на сервер разработчики ВКС занимаются оптимизацией на клиенте. Первая точка оптимизации — это производительность модели. От Google MediaPipe можно уйти в сторону менее функциональных, но более производительных в задаче выделения силуэта моделей. Вторая оптимизация — использование API WebGL в браузере для рисования прямо на видеокарте.
Немного про шумоподавление
Шумоподавление — обязательная фича для ВКС. По традиции реализовать его можно двумя способами: использовать коробочные средства, чтобы получилось средненько, и вложить ресурс разработки для достижения высокого качества.
В браузеры, например, шумоподавление встроено — для включения нужно указать флаг noiseSupression при вызове getUserMedia(). Флаг поддерживается везде, кроме Safari. Работать будет, но качество получится непредсказуемым. Так что для реализации шумоподавления люди натренировали разные нейронки, например RNNoise и DTLN. Их можно встроить на клиенте.
Для увеличения качества звука нужно использовать VAD — это не только сократит отправляемый на сервер трафик, но и улучшит качество звука. Если в кусочке аудио нет человеческого голоса, то он будет просто проигнорирован, и никакое шумоподавление не потребуется. Реализацию VAD тоже можно либо оставить коробочную, либо подсунуть свою.
Для полноты картины отмечу, что в качественном пайплайне обработки звука также должны присутствовать 
Схемы внедрения ВКС
Есть три варианта внедрить видеозвонки.
Купить лицензию у Zoom или его аналогов. Отечественные решения ищутся по запросу «ВКС для бизнеса». Причем обычно такие ВКС можно не только использовать в облаке, но и установить on-premise. Главный плюс здесь — вы сразу получаете качественное готовое решение, в которое помимо видеоконференции включены другие полезные фичи: трансляции, интеграция с переговорками, возможность рисовать на экране и так далее.
Основных минусов, на мой взгляд, два: лицензии на ВКС дорогие и ограничены коробочным функционалом выбранной ВКС. Не получится бесшовно интегрировать видеозвонок в свой интерфейс или добавить произвольную фичу. Да, у некоторых ВКС 
Установить себе в инфраструктуру open-source-медиасервер, например LiveKit или Jitsi, и использовать на клиентской стороне готовые SDK, которые идут в комплекте. Бонусом обязательно будут накладные расходы на настройку и поддержку инфраструктуры.
У решения будет свой предел гибкости: open-source-проекты заточены под сценарий конференц-звонка на пару десятков участников, и сделать что-то вне этого сценария непросто. Например, сложно будет качественно реализовать звонок с суфлером — участником, который видит и слышит всех в звонке, но его самого слышат только выбранные участники.
Клиентский SDK в любом случае отобразит суфлера всем участникам, так что здесь придется делать логику на стороне клиента, что небезопасно для такой чувствительной фичи и затягивает процессы разработки, если у вас несколько клиентов под разные платформы.
Важно, что у open-source-проектов могут возникать проблемы с качеством и производительностью виртуальных фонов, демонстрацией экрана и шумоподавлением. В SDK этих функций может и не быть, но если они все же есть, то обычно реализованы коробочным способом и поэтому уступают платным аналогам. Еще у таких решений вместимость одной видеоконференции ограничена ресурсами одного сервера, сделать конференцию на 500 участников не получится.
В целом, open-source-медиасервер — хорошее решение, если в приложение нужно быстро и недорого встроить небольшие видеоконференции без претензии на высокое качество и высокую способность к кастомизации.
Набраться решимости и написать все самим. Ну, или не совсем все, а написать собственный бэкенд-слой вокруг open-source-медиасервера и сделать свои клиентские SDK. Прежде чем встать на эту скользкую дорожку, нужно трижды подумать, что вам нужно такого, что не может дать условный Jitsi.
На мой взгляд, совпасть должно три главных фактора: полный контроль за качеством решения, возможность встраивать произвольные фичи и наличие бюджета на команду разработки, который будет расти пропорционально требуемой функциональности и качеству. Мы пошли этим путем.
Заключение
Небольшой список характеристик, на которые нужно обратить внимание при выборе ВКС:
-
Качество видео. Есть смысл потестировать звонок в условиях плохого интернета и на слабых девайсах, понаблюдать за качеством и синхронизацией аудио с видео в звонке на 100+ участников, если ВКС допускает такие звонки. У SaaS-решений можно поинтересоваться присутствием в регионах: если у хабаровских пользователей звонок будет идти через Москву, то они это заметят и спасибо не скажут.
При этом нет смысла требовать от ВКС FHD качества: так не может никто в силу физических ограничений пропускного канала со стороны пользователя. Но вот видео 640×480 должно работать стабильно.
-
Качество аудио. Здесь проверяем шумоподавление. Можно провести А/Б-тест с Zoom: провести два звонка в каком-нибудь шумном месте и потом послушать результат на записи.
-
Надежность. При временной недоступности или смене сети на клиенте, например после переключения с Wi-Fi на LTE или поездки в лифте, клиент должен без проблем переподключиться к звонку.
-
Качество демонстрации экрана. Здесь можно провести бенчмарк через показ текстового документа с маленьким шрифтом: полистайте его туда-сюда и сравните качество с Zoom. Отдельно стоит проверить демонстрацию на мобильных клиентах.
-
Качество виртуальных фонов. Опять же, можно сравнить с Zoom: попробуйте разное освещение, разную удаленность от экрана. Как будет вести себя фон, если в кадре два человека? Как увеличивается потребление CPU и GPU в браузере и на десктопном/нативном клиенте?
-
Возможность трансляции (стриминга).
-
Другие фичи. Обычно в рыночных ВКС можно найти ряд полезных опций вокруг функциональности звонка. Например, рисование на экране или в групповом whiteboard, управление удаленным компьютером, возможность дробить одну комнату на несколько или запускать голосовалки. Для корпоративного использования будет полезной интеграция с SSO и Outlook, а также возможность встроиться в переговорки.
Есть более экзотические фичи — например, возможность подключиться к конференции через GSM-звонок или включить автоматическую транскрипцию текста на записи.
ссылка на оригинал статьи https://habr.com/ru/articles/825132/
Добавить комментарий