javascript

Почему бы я не выбрал WebRTC? (но всё-таки выбрал)

  • вторник, 22 июля 2025 г. в 00:00:06
https://habr.com/ru/companies/astralinux/articles/928730/

Привет, Хабр! Я Данил и я разработчик проекта Termidesk Assistant. На нашем проекте мы успешно используем технологию WebRTC, а еще ее используют такие технологические гиганты, как Google Meet, Microsoft Teams, Discord и многие другие.

Хотел бы поговорить о наболевшем, а именно о тех проблемах, с которыми я успел столкнуться, но почему мы все равно остановились на этой технологии. Возможно даже страница будет расширяться теми шишками, которые на меня упали по ходу движения через этот темный лес. Если вы планируете начать проект, который передает аудио и/или видео и ищите открытую технологию для него, ну, и для тех, кто просто интересуется и изучает этот стандарт, то это статья для вас.

Что же такое WebRTC?

WebRTC (Web Real-Time Communication) - открытый стандарт и набор технологий, обеспечивающий прямую (peer-to-peer) передачу аудио, видео и данных в реальном времени между браузерами и приложениями без необходимости установки плагинов или дополнительного ПО.

Минимальный клиент

Для примера давайте напишем базовый клиент на JavaScript и рассмотрим основные функции. Для этого переходите в наш репозиторий на GitFlic. Читайте код, клонируйте проект, запускайте, если есть желание, делайте форк и отправляйте запросы на слияние. 

Кратко опишу имеющиеся ключевые функции и их роли:

  1. navigator.mediaDevices.getUserMedia()

    Захватывает поток с камеры и микрофона. Результат — MediaStream, который мы сразу выводим в <video id="localVideo"> и добавляем в RTCPeerConnection.

  2. new RTCPeerConnection(rtcConfig)

    Создаёт объект соединения, готовый обмениваться медиапотоками и данными. В конфигурации указываем STUN/TURN-серверы для поиска и пробивки NAT.

  3. pc.addTrack(track, stream)

    Добавляет каждую дорожку (video/audio) из локального потока в соединение, чтобы её можно было передать удалённой стороне.

  4. pc.onicecandidate

    Срабатывает при нахождении нового ICE-кандидата (IP-адреса/порта для пиров). Их нужно передать другому участнику через канал сигнализации.

  5. pc.createOffer() / pc.createAnswer()

    Создают SDP-оффер и SDP-ответ — описания возможностей кодеков, протоколов и сетевых каналов. Оффер идёт от инициатора, ответ — от принимающей стороны.

  6. pc.setLocalDescription() / pc.setRemoteDescription()

    Устанавливают локальное SDP и SDP от партнёра. Это фаза согласования (SDP-handshake).

  7. pc.ontrack

    Приходит событие, когда удалённая сторона добавляет дорожки. Полученный MediaStream выводим в <video id="remoteVideo">.

  8. Сигнализация (sendSignal / onSignal)

    WebRTC не включает механизм передачи офферов/ответов и кандидатов — это надо сделать самостоятельно (через WebSocket, HTTP-polling, любой сервер).

Этот шаблон — база для любых WebRTC-сценариев (пиринга, чатов, удалённого рабочего стола и т.д.). Дальше можно добавлять data-каналы, TURN-серверы, управление качеством и шифрование. Файл с кодом можете найти тут.

Если хотите собрать свой клиент-клиент, например, на ВМ, то придется создать сервер, который через websocket-каналы будет перегонять между клиентами данные для инициализации сессии. Код сервера можно найти тут. Это простой пример сервера, основанный на асинхронных aiohttp и asyncio.

По итогу мы получаем вот такую картину:

Не обращайте внимание, что картинка только под Remote Video, я тестировал на ВМ, камера занята хостом и транслируем как раз второму клиенту, что мы и видим - УСПЕХ!

Буду рад видеть ваши pull requests на репозитории. Давайте сделаем из него полноценный открытый видеочат.

С какими сложностями я столкнулся, работая с этой технологией

Ice-кандидаты

Ice-кандидат - это точка входа клиента, которая отправляется другому клиенту для подключения. Главная проблема в том, что если в браузере мы не инициируем устройства, то вместо ip-адреса ice-кандидат будет содержать в себе обфусцированное mDNS-имя типа 123febefbfh.local. Но мы живем в клиентоориентированном капитализме, а значит, клиент прав, а клиент хочет подключение без подтверждения девайсов, поэтому лечим так:

const c = event.candidate;
const o = c.toJSON();
o.candidate = o.candidate.replace(
/ ([0-9a-f-]+\.local) /,
` ${server_ip} `
);

Dummy-stream

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

Это двусторонняя технология, поэтому требуется двусторонняя SDP-сигнализация, то есть SDP-оффер должен содержать хотя бы одну медиасекцию. Многие браузеры завершают подключение, если нет исходящего потока. Решение - dummy-stream, вот как он выглядит в коде:

if (!constraints.audio && !constraints.video) {
   console.log("No devices selected, returning dummy stream");
   const dummy = new AudioContext().createMediaStreamDestination().stream;
   dummy.getAudioTracks().forEach(t => t.enabled = false);
   return dummy;
}

И это всё!

Альтернативные технологии

Разберем технологии, которые могли бы использоваться для архитектуры клиент-сервер-клиент

Таблица 1. Двусторонние технологии для архитектуры клиент-сервер-клиент

Технология

Плюсы

Минусы

SIP + RTP/RTCP

- Стабильный стандарт телефонии с десятилетиями опыта

- Поддерживается большинством VoIP-оборудования (Asterisk, FreeSWITCH)

- NAT traversal через SIP Extensions

- Масштабируемость, поддержка аудио и видео

- Безопасность (шифрование) чаще опциональна (для этого есть есть SRTP)

- Сложная интеграция с браузерами: кодеки не всегда совпадают, нужен софт-клиент или WebRTC-шлюз

XMPP + Jingle

- Легко расширяется под разные задачи (чаты, файлы, звонки)

- Экосистема для чатов, групп, ботов

- Отдельный сервер сигнализации для масштабирования и фильтрации

- Поддержка p2p звонков с медиапотоком (RTP) - p2p-звонки

- Медиа-секция не строго стандартизована, возможны несовпадения кодеков

- Нет нативной браузерной поддержки

- ICE/TURN работает нестабильно

- Шифрование не обязательно

- Мало готовых решений из-за маленького комьюнити

RTMP

- Простой клиент-серверный протокол

- Проприетарный

- Поддержка прекращена Adobe

- Нет p2p

- Задержка высокая (500+ мс)

- Односторонний поток

- Работает только через Flash (помер 🪦)

QUIC + Custom signaling

- UDP из коробки

- Встроенные технологии шифрования (замена TCP+TLS поверх UDP)

- Сервер на Go

- Нет стандарта p2p аудио/видео

- Нет браузерных API для медиа

- Нет NAT traversal

- Нет совместимости между клиентами разных разработчиков

- Поддерживается только Chromium


Таблица 2. Односторонние технологии (поток в одну сторону, трансляция экрана)

Технология

Плюсы

Минусы

HLS (HTTP Live Streaming)

- Легко масштабируется

- Использует HTTP — поддержка всеми браузерами

- Высокая задержка (5–30 сек), хотя в 2019 было анонсировано Low Latency HLS

- Обратная сторона масштабирования — браузерное распределение

MPEG-DASH

- Аналогичные плюсы HLS

- Аналогичные минусы HLS

- Меньшая поддержка, технология устарела (по моему скромному анализу)

RTSP

- Контроль потока: пауза, стоп

- Поддержка временного доступа к файлам на сервере - перемотка назад

- Нет поддержки браузерами

- Специфичен для видеонаблюдения

- Нет сжатия потока, возможна большая задержка

Кастомный протокол через WebSocket + TCP/UDP

- Можете гордиться собой

- Полный контроль над потоком и легкий дебаг

- Это кастом - можно реализовать под свои нужды

- Изобретение велосипеда

- Слабое или отсутствие сжатия, проще использовать готовое

- Нужно учитывать шифрование (WSS, TLS)

- Подходит только для больших озабоченным ИБ корпораций и госструктур

Почему мы все-таки с WebRTC?

Несмотря на преимущества технологий сверху и описанные костыли еще выше, WebRTC побеждает и вот почему:

  • Peer-to-Peer соединение — сервер нужен только обмена данными соединения

  • Низкая задержка (~50–200 мс)

  • Автоматическая обработка NAT traversal

  • Шифрование по-умолчанию

  • Работает из коробки на всех браузерах

  • Помимо передачи аудио и видео трансляций есть встроенный функционал передачи файлов

WebRTC побеждает доступным функционалом, документацией, минимальной задержкой, кроссплатформенностью и кроссбраузерностью  и возможностью p2p-соединения. Если эти особенности отобразить на диаграмме Эйлера, то для этой технологии конкурентов нет.