Создаем живую потоковую CDN для видеотрансляций WebRTC с низкой задержкой +8



Где может потребоваться трансляция с гарантированной низкой задержкой? — на самом деле, много где. Например в онлайн видео-аукционах. Представьте себя ведущим такого мероприятия.
— «Двести тыыыысяч рааааз»
— «Продано!»

С высокой задержкой вы успеете сказать «двести тысяч три» и продать лот еще до того как видео дойдет до участников. Чтобы участники аукциона успели вовремя среагировать, задержка должна быть гарантированно низкой.

В общем, низкая задержка жизненно необходима в любом около игровом сценарии, будь-то онлайн видео аукцион, видеотрансляция скачек с лошадками или интеллектуальная онлайн игра «Что Где Почему» — и там и там требуется гарантированно низкая задержка и передача видео и аудио в реальном времени.

Почему CDN


Один сервер зачастую не способен доставить потоки всем желающим просто по той причине, что все зрители географически распределены и имеют каналы связи разной пропускной способности. Кроме этого, одного сервера может быть недостаточно по ресурсам. Поэтому нужна гроздь серверов, которые занимаются доставкой потока. А это и есть CDN — сеть доставки контента. Контентом в данном случае будет потоковое видео с низкой задержкой, которое передается с веб-камеры транслирующего пользователя зрителям.

В данной статье мы опишем процесс построения географически-распределенной мини-CDN и протестируем полученный результат. Наша CDN будет состоять из четырех серверов и работать по следующей схеме:


В этой схеме используется минимальное необходимое количество серверов: 4.

1) Origin + LB

Это сервер, который принимает транслируемый видеопоток с веб-камеры пользователя.

Отправка потока в браузере может выглядеть так:


Это пример отправки видеопотока с веб-страницы, открытой в браузере Google Chrome. Поток отправляется на тестовый сервер origin.llcast.com.

После того как Origin принял видеопоток, он ретранслирует принятый поток на два сервера Edge1 и Edge2. Сам Origin-сервер потоки не раздает.

Для упрощения, в данной схеме мы возложили на Origin роль балансировщика нагрузки (LB). Он опрашивает edge-ноды и сообщает подключившимся клиентам-зрителям к какой из нод им следует подключиться.

Балансировка работает так:



  1. Браузер обращается к балансировщику (в данном случае совпадает с Origin).
  2. Балансировщик возвращает один из серверов: Edge1 или Edge2.
  3. Браузер устанавливает соединение с сервером Edge2 (в примере), по протоколу Websocket.
  4. Сервер Edge2 отдает видео трафик по WebRTC.

Origin периодически опрашивает серверы Edge1 и Edge2, проверяя их доступность и собирает данные по нагрузке. Этого достаточно чтобы принять решение, на какой из серверов «приземлить» новый коннект. Если один из серверов по какой-то причине недоступен, коннекты будут «приземляться» на оставшийся Edge-сервер.

2) Edge1 и Edge2

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

3) TURN relay — сервер.

Чтобы обеспечить действительно низкую задержку, вся вышеописанная схема доставки аудио и видео работает по протоколу UDP. Однако данный протокол может быть закрыт на сетевых экранах и иногда приходится пускать весь трафик через HTTPS порт 443, который как правило, открыт. Для этого используется TURN relay. Если нормальное соединение по UDP не проходит, видео пойдет через TURN и по TCP. Это неизбежно ухудшит задержку, но если большая часть ваших пользователей-зрителей находится за корпоративными файрволами, то так или иначе придется использовать TURN для WebRTC либо другой способ доставки контента по TCP, например Media Source Extension / Websocket.

В данном случае, мы выделяем 1 сервер специально для прохождения файрволов. Он будет раздавать видео по WebRTC / TCP для тех зрителей, кому не посчастливилось получить нормальное соединение по UDP.

Географическое распределение


Origin-сервер будет находиться в датацентре Франкфурта. В то время как Edge1 сервер будет находиться в Нью-Йорке, а Edge2 будет расположен в Сингапуре. Таким образом мы получаем распределенную трансляцию, охватывающую большую часть земного шара по протяженности.


Установка и запуск серверов


Для недорогого тестирования, возьмем виртуальные серверы Digital Ocean.

Заготовим 4 дроплета (виртуальных сервера) на DO, по 2 Gb RAM каждый. Это обойдется нам в 12 центов в час, а одни сутки примерно в 3 доллара.


В качестве Origin и Edge1, Edge2 — серверов, будет использоваться WebRTC сервер WCS5. Его нужно скачать и установить на каждый из дроплетов.

Процесс установки WCS-сервера выглядит так:

1) Скачиваем дистрибутив.

wget https://flashphoner.com/download-wcs5-server.tar.gz

2) Распаковываем и устанавливаем.

tar -xvzf download-wcs5-server.tar.gz
cd FlashphonerWebCallServer-5.0.2505
./install.sh

Не забываем установить haveged, который может потребоваться для ускорения запуска WCS сервера на Digital Ocean.

yum install epel-release
yum install haveged
haveged chkonfig on
haveged

3) Запускаем.
service webcallserver start

4) Создаем сертификаты Let’sencrypt

wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
certbot-auto certonly

Certbot положит сертификаты в папку /etc/letsencrypt/live

Пример:

/etc/letsencrypt/live/origin.llcast.com

6) Импортируем сертификаты Let’sencrypt

+-- cert.pem
+-- chain.pem
+-- fullchain.pem
+-- fullchain_privkey.pem
+-- privkey.pem
L-- README

Из этих файлов нам потребуются всего два: цепочка сертификатов и приватный ключ:

  • fullchain.pem
  • privkey.pem


Загружаем их через Dashboard / Security / Certificates. Чтобы зайти в админку WCS-сервера, открываем адрес origin.llcast.com:8888, где origin.llcast.com — доменное имя вашего WCS сервера.

Результат импорта выглядит так:


Все готово. Импорт прошел успешно и теперь нужно выполнить короткий тест чтобы убедиться, что все работает. Заходим в Dashboard сервера в демо — пример Two Way Streaming. Нажимаем кнопку Publish и через несколько секунд Play. Убеждаемся что сервер работает, принимает потоки и отдает их на воспроизведение.


После того как проверили один сервер, делаем с остальными двумя (Edge1 и Edge1) тоже самое. Устанавливаем на них WCS5, получаем сертификаты Let's Encrypt и импортируем в установленный WCS-сервер для корректной работы. В завершение каждой установки проводим простой тест, который показывает, что сервер откликается и играет потоки.

В результате мы настроили три сервера, доступные по следующим адресам:


Конфигурация Origin-сервера


Остается сконфигурировать и объединить серверы в CDN. Начинаем с сервера Origin.

Как мы уже писали выше, Origin является в том числе и балансировщиком. Находим в конфигах файл WCS_HOME/conf/loadbalancing.xml и прописываем в нем Edge-ноды. В результате конфиг будет выглядеть так:

<loadbalancer mode="roundrobin" stream_distribution="webrtc">
        <node id="1">
                <ip>edge1.llcast.com</ip>
                <wss>443</wss>
        </node>
        <node id="2">
                <ip>edge2.llcast.com</ip>
                <wss>443</wss>
        </node>
</loadbalancer>

Данный конфиг описывает следующие настройки:

  • На какие ноды должен ретранслироваться видеопоток с Origin (edge1 и edge2).
  • По какой технологии будет происходить ретрансляция (webrtc).
  • Какой порт нужно использовать для Websocket-соединения с edge-нодой (443).
  • Режим балансировки — по кругу (roundrobin).

На текущий момент поддерживается два способа ретрансляции Origin — Edge, которые задаются атрибутом stream_distribution, это:

  • webrtc
  • rtmp

Выставляем здесь webrtc, т.к. нам требуется низкая задержка. Далее необходимо сказать Origin-серверу, что он будет заниматься балансировкой. Включаем балансировщик в конфиге WCS_HOME/conf/server.properties

load_balancing_enabled =true

Кроме этого, назначим балансировщику порт 443, чтобы подключающиеся клиенты могли получать данные от балансера по стандартному порту HTTPS, опять же для успешного прохождения файрволов. В этом же конфиге server.properties добавляем

https.port=443

Далее выполняем перезагрузку WCS-сервера чтобы применить сделанные изменения.

service webcallserver restart

В результате мы включили балансировщик и настроили его на порт 443 в конфиге server.properties, а также настроили ретрансляции Origin-Edge в конфиге loadbalancing.xml.

Конфигурация Edge-серверов


Edge-серверы не требуют особой конфигурации. Просто принимают поток с Origin-сервера, как если бы пользователь отправлял этот поток с веб камеры напрямую на данный Edge-сервер.

Хотя одну настройку добавить все же придется. Нужно переключить Websocket — порт раздающих видео серверов на 443 для лучшей проходимости через файрволы. Если Edge-серверы будут принимать соединения на порту 443 по HTTPS и TURN будет делать тоже самое, мы получим решение с обходом файрвола, которое поможет увеличить доступность трансляции для корпоративных пользователей за файрволами.

Меняем конфиг WCS_HOME/conf/server.properties и выставляем настройку

wss.port=443

Выполняем перезагрузку WCS-сервера чтобы применить сделанные изменения.

service webcallserver restart

На этом настройка Edge-сервера завершена и можно приступать к тестированию CDN.

Трансляция с веб-камеры на Origin


Пользователи нашей мини-CDN делятся на стримеров и плееров. Стримеры — транслируют видео на CDN, а плееры — зрители, это видео играют. Интерфейс стримера может выглядеть так:


Самый простой способ протестировать этот интерфейс, открыть демо Two Way Streaming на Origin-сервере.

Пример стримера на Origin-сервере:

https://origin.llcast.com:8888/demo2/two-way-streaming

Для тестирования нужно установить Websocket соединение с сервером по кнопке Connect и далее отправить видеопоток на сервер по кнопке Publish. В результате, если все верно настроено, поток появится на серверах Edge1 и Edge2 и его можно будет с них проиграть.

Данный интерфейс всего лишь демо HTML-страницы на основе клиентского JavaScript API (Web SDK) и его можно кастомизировать и привести к любому дизайну с помощью HTML / CSS.

Воспроизведение потока с серверов Edge1 и Edge2



Интерфейс плеера можно взять с одного из серверов edge1.llcast.com или edge2.llcast.com — пример Demo / Embed Player

Тестирование можно провести, подключившись напрямую к edge-серверу по этим ссылкам:

Edge1

https://edge1.llcast.com:8888/demo2/embed-player

Edge2

https://edge2.llcast.com:8888/demo2/embed-player

Плеер устанавливает Websocket-соединение с указанным Edge-сервером и забирает с этого сервера видеопоток по WebRTC. Обратите внимание, что на Edge серверах Websocket соединение устанавливается по порту 443, который был сконфигурирован выше для проходимости через файрволы.

Этот плеер можно встроить в сайт в виде iframe либо провести более плотную интеграцию с использованием JavaScript API и разработкой собственного дизайна на HTML / CSS.

Подключение балансировщика нагрузки в плеер


Выше мы убедились в том, что Origin получает видеопоток и реплицирует этот поток на два Edge-сервера. Мы подключились к каждому из Edge-серверов по отдельности и протестировали воспроизведение потока.

Теперь нужно задействовать балансировщик чтобы плеер выбирал Edge-сервер из пула автоматически.

Для этого в JavaScript коде плеера нужно указать адрес балансировщика — lbUrl. Если его указать, то пример Embed Player будет сначала обращаться к балансировщику и брать с него рекомендованный адрес сервера, и уже после этого устанавливать соединение с полученным адресом по протоколу Websocket.

URL балансировщика легко протестировать. Открываем его в браузере:

https://origin.llcast.com/?action=server_list

Результатом будет кусок JSON, в котором балансировщик показывает рекомендуемый Edge-сервер.

{
"server": "edge1.llcast.com",
"flash": "1935",
"ws": "8080",
"wss": "443"
}

В данном случае балансировщик вернул первый сервер edge1.llcast.com и показал, что с сервером можно установить защищенное Websocket соединение (wss) по порту 443.

Если обновлять страницу в браузере, балансировщик будет отдавать то один Edge-сервер, то другой, по кругу. Так работает roundrobin.

Осталось включить балансировщик в плеере. Для этого в коде плеера player.js добавляем параметр lbUrl при установке сессии с сервером:

Flashphoner.createSession({urlServer: urlServer, mediaOptions: mediaOptions, lbUrl:'https://origin.llcast.com/?action=server_list'})

Полный код плеера приведен по ссылке ниже:

https://edge1.llcast.com:8888/client2/examples/demo/streaming/embed_player/player.js

Проверяем работу балансировщика и убеждаемся в том, что он корректно раскидывает соединения по нодам edge1.llcast.com и edge2.llcast.com. Для этого просто обновляем страницу плеера и смотрим в Developer Tools вкладку Network, где виден коннект к балансировщику и к серверу edge1.llcast.com (выделено красным).

https://edge1.llcast.com:8888/client2/examples/demo/streaming/embed_player/player.html



Финальный тест


Мы настроили Orign-Edge, ретрансляции, балансировку и подключение плеера на основе данных балансировщика. Осталось собрать все воедино и провести тест, демонстрирующий работу получившейся CDN.

1. Открываем страницу стримера

https://origin.llcast.com:8888/client2/examples/demo/streaming/two_way_streaming/two_way_streaming.html

Отправляем видеопоток с веб-камеры на Origin сервер, под именем stream1 из браузера Google Chrome.


Заходим в Dev Tools — Network и убеждаемся, что транслирующий браузер подключился к Origin-серверу и отправил на него поток.


2. Открываем страницу плеера на Edge-сервере и играем поток stream1.

https://edge1.llcast.com:8888/client2/examples/demo/streaming/embed_player/sample.html


Видим, что балансировщик выдал edge1.llcast.com и поток в данный момент забирается с него.



Далее обновляем страницу еще раз и балансировщик подключает плеер к серверу edge2.llcast.com, который находится в Сингапуре.


Таким образом мы справились с первой задачей и завершили настройку географически распределенной CDN на базе WebRTC с низкой задержкой.

Далее нужно проверить задержку и убедиться, что она является действительно невысокой относительно RTT. Хорошей задержкой в глобальной сети является стабильная, не превышающая 1 секунды задержка. Проверим что получилось у нас.

Измеряем задержку


Предполагаем что значение задержки будет отличаться в зависимости от того, к какому серверу мы подключились плеером — на Edge1 в Нью Йорке или на Edge2 в Сингапуре.

Для того чтобы иметь какую-то точку отсчета, составим карту RTT, чтобы понимать как устроена наша тестовая сеть и чего ждать от ее участников. Предполагаем, что особых потерь нет и измеряем только время пинга (RTT).


Как видите, наша тестовая сеть совсем глобальная и неоднородная по задержке. Стример и плеер будут находиться в Новосибирске. Пинг до сервера Edge1 в Нью Йорке составит 170 ms, а до Edge2 в Сингапуре 306 ms.

Будем учитывать эти данные при анализе результатов тестирования. Например понятно, что если мы будем транслировать поток на Origin во Франкфурте, передавать его на Edge 2 в Сингапуре и забирать из Новосибирска, то задержка ожидается никак не меньше (90 + 183 + 306) / 2 = 290 миллисекунд.

Проверим предположения на практике. Для получения данных по задержке, воспользуемся тестом с миллисекундным таймером отсюда, используя его как виртуальную камеру.

Отправляем поток на Origin и делаем несколько скриншотов при воспроизведении с
Edge1 — сервера. Слева расположено локальное видео с камеры с нулевой задержкой, а справа видео, которое пришло на плеер через CDN.

Результаты тестов:

Test number

Latency (ms)

1

386

2

325

3

384

4

387

Average latency: 370 ms




Как видите, средняя задержка видеопотока по пути доставки контента Новосибирск (стример) — Франкфурт (Origin) — Нью Йорк (Edge1) — Новосибирск (плеер), составила 370 миллисекунд. RTT по этому же пути = 90 + 84 + 170 = 344 миллисекунды. Таким образом получаем результат 0.9 RTT, что очень и очень неплохо для доставки живого видеопотока, учитывая, что идеальным результатом был бы 0.5 RTT.


Задержка и RTT для маршрута Edge1, Нью Йорк

Теперь заберем тот же поток с сервера Edge2 в Сингапуре и поймем как повышение RTT влияет на задержку. Делаем аналогичный тест с сервером Edge2 и приходим к следующим результатам:

Test number

Latency (ms)

1

436

2

448

3

449

Average latency: 444 ms



Из этого теста видим, что соотношение задержки к RTT около 0.7, т.е лучше, чем в предыдущем тесте.


Задержка и RTT для маршрута Edge2, Сингапур

Результаты тестов дают основание предполагать, что с ростом RTT, значение задержки не будет резко расти, а будет плавно приближаться к 0.5 RTT.

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

В заключение


В итоге, мы развернули три сервера: Origin, Edge1 и Edge2, а не четыре, как обещали в начале статьи. Настройка, конфигурация и тестирование TURN сервера за файрволом сильно увеличивают и без того раздутый материал. В связи с этим полагаем, что есть смысл оформить его в отдельной статьей. Пока же просто не забываем, что четвертым сервером в этой схеме может быть TURN-сервер, который обслуживает WebRTC браузеры по порту 443, обеспечивая проходимость через файрволы.

Итак, CDN для WebRTC потоков настроена и задержка измерена. Финальная схема работы вышла такой:


Как можно эту схему улучшить и расширить? Можно улучшать в ширину. Например добавляем несколько Origin-серверов и ставим балансировщик между ними. Таким образом мы будем балансировать не только зрителей, но и стримеров — тех, кто непосредственно транслирует контент с веб-камер:


CDN пока открыта в демо-доступе на домене llcast.com. Возможно скоро заморозим ее, и демо ссылки работать перестанут. Поэтому если появится желание измерить задержку — добро пожаловать. Хорошего стриминга!

Ссылки


https://origin.llcast.com:8888 — демо сервер Origin
https://edge1.llcast.com:8888 — демо сервер Edge1
https://edge2.llcast.com:8888 — демо сервер Edge2
Two Way Streaming — демо интерфейс трансляции потока с веб камеры на Origin.
Embed Player — демо интерфейс плеера с балансировщиком между Edge1 и Edge2
WCS5 — WebRTC сервер, на основе которого построена CDN




К сожалению, не доступен сервер mySQL