Видеозвонки, WebRTC и браузер: как это работает и как согреть «замерзающую» трансляцию
Для большинства из нас видеосвязь в браузере — нечто вроде черного ящика. Есть изображение собеседника на экране, звук, возможность общения. Но что происходит там, внутри? Об этом сегодня и поговорим.
Статья сделана на основе доклада нашего сотрудника, Кирилла Рогового. Он занимается настройкой видеосвязи в Skyeng и стремится сделать видеосвязь не просто хорошей, а очень хорошей. Он поделился своим опытом, наработками и наблюдениями по WebRTC (Web Real Time Communications), что было оформлено в виде статьи. В ней разбираются не только принципы работы технологии, но и важные моменты процесса улучшения качества видеосвязи. Все это и немного больше — под катом.
WebRTC — что это и зачем мне это знать?
Главная особенность WebRTC в том, что у разработчика не так много вариантов взаимодействия с технологией, по большей части все работает в автоматическом режиме.
Но зачем тогда знать, что происходит «под капотом»? Все просто: знание технических деталей и понимание хотя бы базовых принципов работы технологии дает возможность изменить/адаптировать некоторые моменты под свои нужды в процессе разработки приложений и сервисов.
Для лучшего понимания можно привести аналогию с HTTP. В подавляющем большинстве случаев разработчики используют готовые компоненты — клиентские библиотеки и серверы. Это может быть Fetch API, Node, Nginx и т.п. Вероятно, лишь малая толика читателей этой статьи писала собственные HTTP-сервера, которые парсят сырые запросы по TCP, составляют ответ, делают что-то еще. Но если разработчик знает, как все это функционирует, у него появляется больше возможностей в отладке кода, поиске проблем и способах улучшения производительности своих приложений и сервисов.
Что лежит в основе WebRTC? В первую очередь, способ отправлять и принимать UDP-пакеты в RTP — протоколе верхнего уровня внутри браузеров. WebRTC универсальная технология, которая неплохо справляется с передачей медиа при условии использования аудио- и видеокодеков.
Процесс установки связи происходит следующим образом. Сначала открывается UDP-порт на ПК (конечно, нужно знать IP-порт собеседника), и начинается обмен UDP-пакетами, которые заворачиваются в определенный протокол верхнего уровня, который называется RTP (Real-time Protocol). Большая часть этого процесса происходит «под капотом», разработчик же лишь заполняет некоторые пробелы.
Как работает WebRTC?
Все начинается с захвата камеры. Итак, есть API браузера, который дает возможность просить у пользователя доступ к камере и микрофону. После одобрения запроса формируется медиастрим. Передавать поток собеседнику напрямую не выйдет, поскольку объем передаваемых несжатых данных довольно велик. В обычной ситуации поток видео — это тридцать картинок в секунду, с информацией о каждом пикселе в каждой картинке — фрейме. Это BMP в мире видео. Соответственно, видео длиной в 1 секунду составляет около 36 МБ, т.е. битрейт в этом случае составляет 288 Мбит/с. А вот ширина большинства каналов — всего 100 Мбит/с. Поэтому приходится задумываться о сжатии, кодировании исходного видео.
Кодирование. На этом этапе очень важно сжимать видео, не теряя качества. С этим справляется, например, кодек VP9, который разработан для кодирования видео в реальном времени. Он позволяет передавать видео с качеством 720p и fps 30 c битрейтом 1,5 Мбит/с. Как это реализовано? Все просто: сравниваются соседние кадры с выделением diff (информация об отличиях двух соседних кадров). Здесь стоит помнить о двух объектах. Первый — keyframe — полная информация о картинке, второй — interframe — информация о разнице между соседними картинками, кадрами.
NB! Разные браузеры могут поддерживать разные кодеки, VP9 приведен лишь в качестве примера.
Запаковка информации в RTP-протокол. Сразу после кодирования потока отправить его клиенту, например, по UDP, нельзя. Нужно решить несколько проблем, без чего передача данных невозможна:
- Хранение информации о порядке следования пакетов данных.
- Хранение информации о тайминге, что необходимо для синхронизации видео и аудио.
- Хранение информации о треках, что дает клиенту возможность отличать разные дорожки, выделять произвольные данные.
Вся эта информация добавляется к передаваемым данным, но оверхед небольшой — его объем в среднем составляет 5% от исходного объема данных. Кроме того, RTCP (RTP Control Protocol) дает возможность обмениваться информацией о потерянных пакетах и статистике их получения в неидеальных сетях.
Твой пакет был очень вкусным, %username% // Credit
RTCP — это спецификация протокола в рамках RTP и библиотека для «си» от Google (lib WEBRTCP или lib GEGL). Собственно, это код на «си» в браузере Firefox или Chrome.
Отправка данных. По сети данные уходят в виде UDP-пакетов. UDP, а не TCP используется потому, что у UDP минимальный интервал между пакетами. К слову, DDoS осуществляется обычно по UDP. Но в нашем случае минимальный интервал и скорость передачи — это преимущество. Недостаток — нет гарантий, что отправленный пакет дойдет до адресата.
Распаковка. Полученные пакеты нужно расставить в правильном порядке и передать в декодер, на выходе которого — чистый видеопоток.
Подключение к элементу видео. Последний этап — получение изображения собеседника. Если данные передаются без проблем — видео транслируется. Если передача прекращается, мы видим последний из переданных кадров.
NB! Видеопоток шифруется сертификатом браузера. Если разработчик не добавил собственный сертификат, то будет использован стандартный сертификат браузера. Поэтому следует учитывать, что если сертификат предоставлен с доступом к приватному ключу (что практикуется в Казахстане, например), то оператор сертификата сможет подключиться к передаваемому видео и аудиопотоку.
Если бы наш мир был идеальным, то ягнята резвились бы рядом со львами на солнечных лужайках, а передача видео происходила бы в том порядке, что описан выше. Статью можно было бы на этом и закончить. Но до идеала далеко, так что придется поговорить о проблемах и их решении.
Потери пакетов, фриз и вот это все
К сожалению, при передаче данных далеко не все пакеты доходят до получателя. Иногда они теряются. Почему? Есть три основных категории причин:
- Они никуда не уходят. Это явление называется Random loss или Lossy network. Пример — когда пользователь и роутер находятся в одном помещении, но между ними стена. В итоге теряются рандомные пакеты. Еще можно включить микроволновку, и потери будут еще более масштабными. Hint — если у вас назначено важное собеседование во видеосвязи, не стоит в это же время рядом разогревать супчик.
- Пакеты дропаются по ошибке. Они могут задерживаться и не приходить вовремя по самым разным причинам — программным и аппаратным. Перегрелось оборудование, заглючил софт — что угодно, и пакеты теряются.
- Network congestion или перегрузка сети. Между отправителем и получателем находится много разных сетевых узлов, которые принимают и отправляют трафик. В основном, это задача маршрутизаторов. Но эти устройства рассчитаны на определенный объем принимаемых данных. Если узел получает больше трафика, чем он в состоянии обработать, то в лучшем случае формируется буфер, который дает возможность справиться с перегрузкой, что приводит к задержке в передаче пакетов, но передача все же идет. В худшем — пакеты перестают отправляться вообще.
Твой пакет не пройдет! // Credit
Что происходит с WebRTC трансляцией, если теряется относительно небольшая часть пакетов? RTCP повторно запрашивает пакеты, на что нужно время. Так, если задержка в обычной ситуации составляет 30 мс, то при повторном запрашивании пакета уйдет уже 60 мс. Видео фризится, приостанавливается, пользователь начинает немного нервничать.
Но если потери существенные, приходится запрашивать новый keyframe, а не interframe. В этом случае лаг уже гораздо более продолжительный. В особо тяжелых случаях связь приходится устанавливать по-новой, пользователь недоволен и ругается.
От разработчика в такой ситуации ничего не зависит, все это — «под капотом» технологии WebRTC.
Можно ли что-то сделать?
Да, конечно. Решение есть всегда, а в случае WebRTC их сразу четыре.
№1. Jitter buffer или интерполяция
Демонстрация картинки пользователю с небольшой задержкой. Так, если задержка в сети — 30 мс, изображение будет показываться с задержкой в 90 мс. До определенного момента никто ничего не будет замечать, общение будет комфортным. В некоторых сетевых играх вроде CS и Dota используется тот же принцип.
Предварительная задержка дает дополнительное время (те самые 60 мс из нашего примера) на решение проблем с потерей пакетов. Если объем потерянных пакетов небольшой, пользователь ничего не заметит. В сложных ситуациях фриз все равно возникнет, но будет относительно коротким.
Это решение уже встроено в технологию WebRTC.
№2. Уменьшение битрейта
Для иллюстрации решения возьмем простую модель. Битрейт = количество кадров в секунду * на качество * разрешение. При изменении параметров битрейт будет либо расти, либо падать. В сложных ситуациях достаточно понизить битрейт, и связь будет оставаться на приемлемом уровне. Качество картинки снизится, но общаться можно без проблем. Объяснение простое — чем ниже битрейт, тем выше вероятность, что пакеты не попадут в буфер маршрутизатора, который находится под большой нагрузкой.
Несмотря на то, что это тоже встроенное в WebRTC решение, разработчики уже могут на что-то влиять. Так, при установке сессии можно установить лимит, за который WebRTC не сможет выйти. В Skyeng установлено 256 кб/с при максимальном разрешении 640×480, чего хватает для того, чтобы передавать картинку относительного качества при низкой загрузке на канал. Этот метод в большинстве случаев помогает избежать Network congestion.
№3. Forward error correction
Метод предусматривает дублирование важных данных кодеком. В этом случае немного увеличивается битрейт, но зато есть ненулевая вероятность восстановить утерянный пакет. Дубликаты выполняются в низком качестве, так что если данные теряются, картинка транслируется немного менее качественная, чем оригинал. Но зато нет фриза.
Forward error correction работает в автоматическом режиме, но разработчик может эту опцию отключать, если твердо уверен, что она не нужна.
№4. Сетевой тюнинг
А вот здесь разработчик может развернуться — все зависит только от него. Например, можно выбирать лучший сетевой маршрут. Если компания купила серверы в разных регионах, например, Москве, США, Европе, то можно задавать наиболее удобный путь, по которому данные будут передаваться. В Skyeng маршрут выбирается по принципу «минимальная сумма пингов» при прокладывании канала «ученик-учитель». Если бы маршрут прокладывался в автоматическом режиме, качество видео страдало бы.
Кроме сетевых маршрутов настраивать можно сервера и маршрутизаторы. И чем больше разработчик знает о процессах, происходящих «под капотом», тем больше у него возможностей влиять на процесс передачи данных на «последней миле». Так, мы недавно обнаружили, что пакеты теряются на наших серверах, поскольку буферы слишком маленькие и ОС дропает все пакеты выше лимита. После того, как мы настроили буферы, качество связи существенно улучшилось.
Стоит учитывать, что контроль у разработчика есть лишь над собственными серверами или окружением пользователя.
Собственно, это и все на сегодня. Единственное, стоит еще добавить, что WebRTC постоянно развивается. Это стабильная технология, которую поддерживает масса браузеров, включая Safari. Насколько можно судить, у WebRTC впереди — много лет активной работы.