Сложно представить видеостриминг без записи. Запись видеопотоков — одна из самых востребованных функций, которая может использоваться в различных сценариях работы, например, запись видео сообщений, архив видеонаблюдения, запись интервью, съемка на улице и многие другие.
Можно использовать два подхода для организации записи:
- записывать видеопоток и хранить записи на устройстве пользователя (камера с SD картой, смартфон с которого блогер ведет онлайн трансляцию, ноутбук и т.п.);
- стримить и записывать видеопоток сразу на сервере, не храня на клиенте.
Для первого подхода существенным минусом является тот факт, что при потере или повреждении устройства с помощью которого велась запись — камера, мобильный телефон, ноутбук и т.п. — будет потерян и архив записей. Зато есть и большой плюс: не нужно выделять серверные мощности под запись и хранение записей.
Для второго подхода большим плюсом будет значительное увеличение надежности в хранении записей, теперь записи не будут зависеть от возможных повреждений клиентских устройств. Но, для хранения архива записей потребуется место на сервере, и некоторые ресурсы процессора и оперативной памяти будут расходоваться на обеспечение процесса записи. Поэтому, минус этого подхода — выделение дополнительных серверных ресурсов.
В Web Call Server реализован второй подход, при котором записи хранятся на сервере. Браузер или мобильное приложение устанавливает соединение с WCS и отправляет WebRTC видеопоток. WCS записывает и сохраняет этот видеопоток в файловой системе сервера в виде файлов WebM или mp4, после чего эти записи доступны для скачивания и/или дальнейшей обработки. Например, можно переместить их в облачное хранилище Amazon S3.
Контейнер, в который ведется запись, зависит от кодека, использованного при публикации потока. Форматы записи:
- MP4 для кодеков H.264 + AAC;
- WebM для кодека VP8 + Vorbis
При одновременной записи большого количества публикаций сохранение файлов на диск по окончании записи может занимать существенное время. Поэтому, при выборе носителей для хранилища записей стоит обратить внимание не только на объем, но и на скорость работы (IOPS) и отдавать предпочтение наиболее быстрым вариантам.
Итак, задача — на вашем WCS сервере опубликовано 100 WebRTC стримов и вам нужно организовать их запись. Такой вариант работы может применяться для организации, например, системы видеонаблюдения, когда важно не только смотреть стрим в реальном времени, но и сохранить записи на случай каких-либо инцидентов. Исторически сложилось так, что в вашей инфраструктуре используются инстансы на AWS, большинство из которых это c5.4xlarge и инстанс для WCS не исключение. Решение по организации записи потоков не выглядит сложным — всего лишь одна REST команда для каждого потока и все работает. Но вдруг, спустя каких-то несколько минут после начала записи, на ваш телефон начинают сыпаться алармы от системы мониторинга — все плохо, все сломалось!
Итак, давайте проведем тесты и разберемся, почему такое могло произойти.
План тестирования
- Опубликовать поток с камеры на сервере WCS #1.
- С помощью веб-приложения Console забрать поток с сервера WCS#1 на WCS#2. При этом на сервере WCS#2 создается 100 входящих WebRTC потоков, которые будут записаны.
- С помощью скрипта, использующего REST запросы, начать запись входящих на WCS#2 WebRTC потоков.
- Выбрать случайный стрим на WCS#2 и визуально оценить деградацию потока (или ее отсутствие) во время работы записи потоков.
- Остановить запись WebRTC потоков на WCS#2 с помощью скрипта, использующего REST запросы.
- Оценить нагрузку на сервер WCS#2 по значениям метрик, собираемых системой мониторинга Prometheus для сервера WCS#2.
Тестирование будем считать успешным, если будут сформированы файлы записей и не будет зафиксировано деградации потоков в результатах мониторинга или визуально.
Подготовка к тестированию
Для тестирования записи мы использовали два AWS инстанса c5.4xlarge со следующими характеристиками:
- 16 vCPU;
- 32 Gb RAM;
- 10Gbps;
Web Call Server адаптирован для запуска в окружении Amazon EC2 в несколько кликов, так же можно и вручную развернуть WCS на инстансе Amazon EC2. После установки сервера мы немного оптимизировали настройки — включили ZGC и указали, сколько процессорных потоков использовать для работы рекордера:
file_recorder_thread_pool_max_size=8
Примеры файлов с настройками WCS — flashphoner.properties и wcs-core.properties можно скачать в разделе «Полезные файлы».
Инстанс WCS#2, на котором будет проводиться тестирование записи подключаем к мониторингу Prometheus+Grafana. В этом тестировании будем отслеживать следующие метрики
- Загрузка процессора:
node_load1 node_load5 node_load15
- Использование физической памяти для Java:
core_stats{instance="your.WCS.server.name:8081", job="WCS_metrics_statistic", param="core_java_freePhysicalMemorySize"} core_stats{instance="your.WCS.server.name:8081", job="WCS_metrics_statistic", param="core_java_totalPhysicalMemorySize"}
- Паузы в работе ZGC:
gc_stats{instance="your.WCS.server.name:8081", job="WCS_metrics_statistic", param="gc_last_pause_ms"}
- Пропускная способность сети:
network_stats
- Количество входящих WebRTC стримов:
streams_stats{instance="your.WCS.server.name:8081", job="WCS_metrics_statistic", param="streams_webrtc_in"}
- Количество и процент деградировавших стримов:
degraded_streams_stats{instance="your.WCS.server.name:8081", job="WCS_metrics_statistic", param="degraded_streams"} degraded_streams_stats{instance="your.WCS.server.name:8081", job="WCS_metrics_statistic", param="degraded_streams_percent"}
- Количество открытых рекордеров (количество стримов для которых идет запись)
recording_stats{instance="your.WCS.server.name:8081", job="WCS_metrics_statistic", param="recording_sessions"}
- Использование дискового пространства (в %)
100 - node_filesystem_avail_bytes{instance='your.WCS.server.name:9100' , fstype!='tmpfs'} / node_filesystem_size_bytes * 100
Готовую панель для Grafana можно скачать в разделе «Полезные файлы».
Тестирование
После завершения всех подготовительных работ приступаем к тестированию.
Потоки для записи создадим с помощью веб приложения Console:
1. На первом сервере WCS#1 откройте приложение Console через HTTP http://your.WCS.server.name:9091/client2/examples/demo/streaming/console/console.html
2. Укажите доменное имя или IP адрес первого сервера и нажмите кнопку «Add node». Это будет сервер источник WebRTC потока. Затем аналогично подключите к консоли второй WCS сервер, который будет захватывать потоки для записи.
3. Для первого сервера запустите стандартный пример Two-way Streaming и опубликуйте поток с веб камеры. Имя потока может быть любым.
4. В приложении Console выберите сервер WCS#2, нажмите кнопку «Pull streams» и задайте параметры захвата потоков:
- Choose node — выберите первый сервер;
- Local stream name — укажите имя потока на WCS#2 сервере в который будет захвачен поток от первого. (к имени потока будет добавлен индекс, соответствующий номеру захваченного потока). Указанное имя потока будет в дальнейшем использоваться в скрипте для записи потоков (у нас «Stream-«);
- Remote stream name — укажите имя опубликованного на первом сервере потока;
- Qty — укажите количество зрителей (для нашего тестирования 100).
Нажмите кнопку «Pull» для запуска захвата потоков.
Запись захваченных потоков запускаем с помощью скрипта:
#!/bin/bash qty=100 for i in `seq 1 $qty`; do DATA='{"name":"Stream-'$i'", "published":true, "display":["metrics"]}' curl --data "$DATA" -X POST -H "Content-Type: application/json" http://localhost:8081/rest-api/stream/find > stream.txt mediaSession=`grep -P -o '(?<=\"mediaSessionId\"\:\").*(?=\"\,\"name\")' stream.txt` DATA_rec='{"mediaSessionId":"'$mediaSession'", "config":{ }}' curl --data "$DATA_rec" -X POST -H "Content-Type: application/json" http://localhost:8081/rest-api/stream/startRecording -nostdin -nostats </dev/null >/dev/null 2>&1 & done
где:
qty — количество создаваемых рекордеров.
С помощью REST запроса /stream/find мы получаем список стримов, которые существуют на сервере, чтобы определить id медиасессии, затем запросом /stream/startRecording запускаем запись потока в соответствующей медиасессии.
Результат тестирования можно видеть на скриншоте:
Здесь можно заметить, что спустя около 15 минут после начала теста, файлы записей заняли 100% дискового пространства. По этой причине, не смотря на небольшую нагрузку на ресурсы сервера (Load average 1 в пике менее 8 единиц) и отсутствие деградировавших потоков, тест нельзя считать пройденным. Причина кроется в том, что для инстанса c5.4xlarge AWS предоставляет всего лишь 8 Гб дискового пространства. Поэтому достаточно большое число записей при тесте очень быстро его занимает полностью.
В первом тесте мы публиковали поток при помощи стандартного примера Two-way Streaming и поток был опубликован с параметрами 320×240, это достаточно «маленький» поток, который особо не нагружает сервер. Битрейт такого потока будет около 0,5 mbps. Создаваемый файл записи для одного такого потока занимает около 65 килобайт дискового пространства в секунду.
Давайте посмотрим, как поведет себя наш сервер, если мы опубликуем более «жирный» поток — 1080р (FullHD) со средним битрейтом 2,5 mbps. Для одного потока с такими параметрами прирост файла записи будет уже около 300 килобайт в секунду, что гораздо быстрее, чем для потока 240p.
Публикацию потока в этом случае запустим с помощью примера «Media Devices» где и зададим размеры кадра 1920х1080:
Затем запустим тест аналогично предыдущему. Результат теста на скриншоте ниже:
Тест с потоком 1080p ожидаемо показал результат аналогичный предыдущему тесту — записи снова заняли 100% доступного дискового пространства и поэтому продолжение работы сервера оказалось невозможным. В случае «большого» потока время в течении которого записи заняли все доступное место составило около 5 минут. Это в три раза быстрее, чем в предыдущем тесте с «маленьким» потоком. Итак, мы на практике убедились, что чем больше разрешение и битрейт потока, тем больше файлы записи занимают дискового пространства.
Немного изменим условия теста. В процессе тестирования будем останавливать запись и перемещать сформировавшиеся файлы записи. Результаты такого теста ниже:
Этот тест можно считать пройденным. Нагрузка на сервер была небольшой (Load Average 1 менее 8 единиц), деградировавших стримов не зафиксировано. В случае использования такого сценария работы следует учитывать, что при записи «больших» потоков, перемещать «кусочки» видео нужно чаще, чем для более «легких» потоков (с небольшим разрешением и битрейтом).
Для нарезки фрагментов записи не обязательно останавливать запись, как мы делали в тесте, можно воспользоваться функцией «Ротация файлов записей«, которая настраивается при помощи параметра record_rotation в файле flashphoner.properties.
Например, настройка
record_rotation=20
определяет длительность фрагмента в 20 секунд, а настройка
record_rotation=10M
задает объем фрагмента в 10 мегабайт.
В итоге, можно сделать вывод, что инстанса c5.4xlarge достаточно для длительной записи 100 WebRTC потоков, при условии использования внешнего хранилища или дополнительного диска для записей.
Подтвердим последнее утверждение тестом. Повторим тест с подключенным к инстансу диском типа «st1» на 125 Гб. AWS заявляет для таких дисков производительность 500 IOPS, что в несколько раз больше, чем производительность SSD диска, который поставляется с инстансом по умолчанию.
За время теста было израсходовано около 5% емкости подключенного диска. Тест пройден успешно. Деградировавших стримов зафиксировано не было.
Таким образом, мы подтвердили утверждение, что инстанса c5.4xlarge достаточно для длительной записи 100 WebRTC потоков, при условии использования дополнительного диска для записей. В этом случае результаты теста меньше зависели от скорости работы процессорных потоков и оперативной памяти. Ключевой метрикой была скорость записи на диск и количество свободного дискового пространства. На вашем оборудовании при проведении вы можете получить другие результаты. Проще говоря, тест на мощных «железных» серверах покажет лучшие значения, чем тест на средних «виртуалках». Поэтому при проведении теста, нужно стараться создать окружение близкое к вашей реальной задаче.
Хорошего стриминга!
Полезные файлы
Панель для Grafana:
Настройки WCS:
Скрипты для теста:
Запуск записи:
Остановка записи: