- PVSM.RU - https://www.pvsm.ru -
Сегодня видеоконференции — стандартный инструмент для бизнеса, образования и повседневного общения. Одной из ключевых технологий, лежащих в их основе, является WebRTC — открытый стандарт для обмена аудио, видео и данными в реальном времени. Однако качество работы и масштабируемость зависят от выбранной топологии взаимодействия участников.
WebRTC — это набор API и протоколов, позволяющих браузерам и мобильным приложениям напрямую обмениваться мультимедийными потоками.
Для того чтобы прямое соединение между двумя браузерами, у которых нет белого IP, было возможно, используется технология ICE.
ICE (Interactive Connectivity Establishment) отвечает за выбор оптимального маршрута между клиентами. STUN помогает определить внешний IP и порт за NAT, а TURN служит запасным вариантом, обеспечивая ретрансляцию трафика через сервер.
Собранной информацией с адресами для подключения (ICE-кандидатами) нужно обменяться между участниками соединения. Передать их можно любым доступным способом, но обычно для этого используют WS/HTTPS-сервер, который не участвует в передаче медиа, но обеспечивает организацию сессий.
Также участники должны договориться, как именно будет происходить передача. Для этого используется протокол SDP. Session Description Protocol описывает кодеки, разрешения, форматы и другие параметры медиа.

Когда речь заходит об организации видеоконференций, где участвует более двух клиентов, есть различные варианты схем такого взаимодействия.
Самая простая топология, которая подразумевает прямую передачу потоков от каждого участника ко всем остальным.

Самая простая в реализации схема, не требующая сервера.
Главным минусом такой реализации является быстро растущий трафик и нагрузка на клиентские устройства.
Давайте заглянем под капот браузера, как происходит кодирование стримов для отправки другим участникам?
Кажется интуитивно логичным, что браузер кодирует видео с источника (камеры), а потом посылает его во все подключения других участников.

Но в реальности видео отдельно кодируется для каждого соединения, и нагрузка увеличивается прямо пропорционально количеству участников.

Пример: при 4 участниках каждый браузер должен кодировать и отправлять 3 потока и декодировать ещё 3. CPU достигает ~60% загрузки, а суммарный трафик — 6 Мбит/с.
На практике это означает, что при использовании такой топологии видеоконференции более чем на 8 участников организовать не получится.
SFU (Selective Forwarding Unit) — топология, при которой используется сервер, который перенаправляет потоки между участниками. При этом сам сервер не декодирует и не кодирует стримы в процессе.

Такой подход позволяет снять часть нагрузки с клиента. При этом сервер может гибко перенаправлять потоки, не тратя ресурсы на их перекодирование.
На клиенте остается нагрузка, связанная с декодированием каждого входящего потока по отдельности.
Входящая нагрузка на сеть также остается большой.
Связи внутри сервера увеличиваются в прогрессии n2 (где n — количество участников).
Пример: при 4 участниках клиент кодирует один поток и получает три. CPU достигает ~40% загрузки, а суммарный трафик — 4 Мбит/с. Сервер лишь маршрутизирует с нагрузкой ~10% CPU.
При использовании такой топологии получится организовать видеоконференцию примерно на 50 участников.
Но благодаря гибкости такого подхода нагрузку и трафик можно сокращать, увеличивая тем самым количество участников.
Нет смысла отображать на экране больше 30 видео. Это неудобно для пользователя. Следовательно, с участников, которые не отображаются, можно получать только аудио. Тем же, кто смотрит конкретное видео на весь экран, достаточно получать только это видео. В свою очередь, можно не получать аудио с тех, у кого отключен микрофон.
Если у какого-то потока в SFU нет ни одного получателя, его можно и не получать в SFU.
Еще один способ снизить нагрузку и трафик у клиентов — это уменьшение качества видео в потоке. Например, нет смысла получать качество потока выше, чем размер видеоэлемента в пикселях, а при раскладке в 30 видеоэлементов это размер небольшой. И только при развороте одним из участников видео на весь экран можно попросить отправителя повысить качество. Но при таком раскладе потребление трафика подскочит у всех участников.
Чтобы таких скачков трафика не происходило, а получатели всё могли выбирать качество, используют Simulcast. Участники отправляют всем видео в разном качестве, а получатели сами выбирают, какое им подходит.

В этом случае нагрузка и трафик на отправителе повышается, т. к. нужно кодировать три отдельных потока для разных вариантов качества. Количество связей в SFU также растет в разы.
SVC (Scalable Video Coding) — это еще один подход для решения той же задачи, но в этом случае все варианты качества кодируются вместе и расположены в одном потоке, причем все они являются производными из одного основного. Все участники могут получать от SFU только слой с необходимым вариантом качества в отдельности.

Единственным недостатком является ограничения кодеков. Полноценно кодирование в SVC сейчас поддерживают только VP9 и AV1, причем только в Хроме. Для участников без поддержки кодирования в SVC придется использовать Simulcast или единственный вариант качества.
MCU (Multipoint Control Unit) — топология, при которой сервер объединяет все входящие потоки в один и отправляет каждому участнику только итоговый микс.

Минимальная нагрузка на клиентов.
Отлично для слабых и мобильных устройств.
Минимальный трафик.
Высокая нагрузка на сервер, т.к. сервер должен декодировать все потоки, объединить и кодировать в один.
У конечного пользователя пропадает возможность выбирать, чье видео смотреть, только общее.
Пример: клиент кодирует один поток и получает один общий и тратит на это ~20% CPU с суммарным трафиком — 2 Мбит/с. Сервер, напротив, работает на 100% CPU.
С таким подходом количество участников в видеоконференции, теоретически, может быть неограничено.
Прежде всего, производить объединение видео и аудио;
Обеспечивать возможность подключения/отключения источников в реальном времени;
Синхронизировать аудио/видео и разные источники между собой (это может быть нетривиальной задачей);
Обработка должна происходить с минимальной задержкой;
Должна быть возможность управления раскладками (мозаика, активный спикер, «картинка в картинке» и т. д.);
В идеале, для перекодирования должно использоваться GPU;
Классические способы организации MCU сложные в реализации и доработке.
Профессиональные видеомикшеры и платы видеозахвата обеспечивают высокое качество и низкую задержку, но стоят дорого и плохо масштабируются. Реализовать дополнительный функционал на их основе невозможно.
Использование мультимедийных библиотек, которые позволяют собирать потоки и выводить общий видеоряд. Это гибко, но требует больших доработок этих библиотек и глубокой экспертизы.
Мы подумали — вот бы был движок, который:
умеет принимать и отдавать потоки по RTP/WebRTC;
имеет богатые возможности манипулирования визуалом и звуком, используя для этого GPU;
сам автоматически синхронизирует всё между собой;
имеет удобный канал управления на лету, а не запускается в виде процесса с заранее заданными параметрами.
Но ведь он есть — браузер у участников как раз этим и занимается.
Можно использовать сам браузерный движок (например, Chromium в headless-режиме) в качестве «сборщика» видеопотоков. Браузер умеет декодировать WebRTC-потоки, рендерить их на canvas или производить захват экрана, объединять аудио через Web Audio API и выдавать обратно в WebRTC-стрим. Такой метод позволяет:
ускорить разработку за счёт готовых API и знакомого стека для разработчиков;
использовать GPU и аппаратное ускорение;
добавлять интерактивные элементы (оверлеи, титры, визуальные эффекты) на видео;
управлять происходящим на видео в прямом эфире, например через WebSocket.
Давайте поговорим о реализации такого функционала. Для этого нам понадобятся следующие браузерные API:
WebRTC API (https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API [1])
MediaDevices (https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices [2])
MediaStream (https://developer.mozilla.org/en-US/docs/Web/API/MediaStream [3])
Web Audio API (https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API [4])
В таком подходе MCU выступает служебным участником видеоконференции. Все участники подключаются не друг к другу, а к нему, получая в ответ общий «склеенный» поток. MCU получает MediaStream из RTCPeerConnection каждого участника и отображает их в сетке своего лейаута так же, как и обычные участники, после чего производит захват экрана с помощью getDisplayMedia и передает стрим в RTCPeerConnection каждого участника.
Для аудио MCU может собирать общий микс, но нужно понимать, что каждому вещающему нужно отправлять индивидуальную версию (исключая его собственный голос, чтобы не было эха).

Для объединения аудио MCU преобразует MediaStream каждого участника в MediaStreamAudioSourceNode. Кроме того, для каждого участника создается свой MediaStreamAudioDestinationNode, куда подключаются MediaStreamAudioSourceNode всех участников, кроме него самого. Полученный в итоге стрим передается в RTCPeerConnection этого участника.
В этом случае почти вся нагрузка ложится на MCU. Он должен декодировать потоки каждого участника и кодировать общий поток для каждого участника.
Пример: при 4 участниках клиент кодирует один поток и получает один. CPU достигает ~20% загрузки, а суммарный трафик — 2 Мбит/с. MCU получает 4 потока и отдает 4. CPU на MCU достигает ~260% загрузки.
После настройки использования GPU нагрузка перераспределяется.
Пример: при 4 участниках клиент кодирует один поток и получает один. CPU достигает ~20% загрузки, а суммарный трафик — 2 Мбит/с. MCU получает 4 потока и отдает 4. CPU на MCU достигает ~150% загрузки. GPU на MCU достигает ~30%.
Чтобы нивелировать недостатки, которые появляются при использовании MCU, можно воспользоваться комбинированным подходом, когда используется и MCU, и SFU. MCU не нужно будет кодировать общий стрим для каждого участника. Кроме того, появляется возможность гибкого перенаправления и выбора между индивидуальными потоками и общим с MCU.

В таком подходе MCU выступает служебным участником видеоконференции, который, как и остальные, подключается к SFU. MCU получает MediaStream каждого участника из RTCPeerConnection SFU и отображает их в сетке своего лейаута так же, как и обычные участники, после чего производит захват экрана с помощью getDisplayMedia и передает стрим в RTCPeerConnection SFU.
Для объединения аудио MCU преобразует MediaStream каждого участника в MediaStreamAudioSourceNode. Кроме того, для каждого участника создается свой MediaStreamAudioDestinationNode, куда подключаются MediaStreamAudioSourceNode всех участников, кроме него самого. Полученный в итоге стрим передается в RTCPeerConnection SFU.
Такой подход предоставляет целый ряд преимуществ:
Вариативность получения стримов. Например, даже при просмотре пользователем общего потока с MCU можно реализовать разворот индивидуального видео на весь экран;
Показывать MCU индивидуально, а не всем, например, только для слабых и мобильных устройств;
MCU стрим можно использовать в качестве серверной видеозаписи, которая сразу готова.
Пример: при 4 участниках клиент кодирует один поток и получает один. CPU достигает ~20% загрузки, а суммарный трафик — 2 Мбит/с. CPU SFU равняется ~10%. MCU получает 4 потока и отдает 1. CPU на MCU достигает ~115% загрузки. GPU на MCU достигает ~25%.
Комбинированный подход показал себя как наиболее гибкое решение: удалось равномерно распределить нагрузку между клиентами и серверной частью, максимально снизив требования к пользовательским устройствам. Использование браузерного движка в роли MCU стало удачным выбором — это дало возможность ускорить разработку, использовать GPU и знакомые веб-API, а также обеспечить удобство управления и расширяемость системы. Такой подход открывает путь к созданию более масштабируемых и адаптивных платформ видеоконференций.
Автор: beatlejute
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/webrtc/434902
Ссылки в тексте:
[1] https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API
[2] https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
[3] https://developer.mozilla.org/en-US/docs/Web/API/MediaStream: https://developer.mozilla.org/en-US/docs/Web/API/MediaStream
[4] https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API: https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
[5] Источник: https://habr.com/ru/companies/tensor/articles/960910/?utm_campaign=960910&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.