Pull to refresh
0

Проблема первого зрителя, или непростая конвертация WebRTC видеопотоков в HLS

Reading time 7 min
Views 5.2K


Егор закрыл крышку ноутбука и потер красные от недосыпа глаза. "Клиенты продолжают жаловаться на зависания стрима, новый пакет исправлений совсем не помог! Что же делать с этим (censored) HLS?" — произнес он в пустоту кабинета.


Браузер это не только гипертекст, но и стример


Плеерами браузеры обзавелись достаточно давно, а вот с энкодером видео и стримингом история другая. Сейчас почти в любом браузере свежей версии можно найти модули энкодинга, стриминга, декодирования и воспроизведения. Эти функции доступны через JavaScript API, а реализация называется Web Real Time Communications или WebRTC. Эта встроенная в браузеры библиотека умеет достаточно много: захват видео с встроенной, виртуальной или USB камеры, компрессия кодеками H.264, VP8, VP9, отправка в сеть по SRTP протоколу, т.е. функционирует как софтверный видеокодер-стример. В итоге видим браузер, у которого под капотом работает что-то похожее на ffmpeg или gstreamer, который хорошо жмет видео, стримит по RTP и играет видеопотоки.


WebRTC дает простор для реализации разнообразных стриминговых кейсов на JavaScript:


  • стримить поток из браузера на сервер для записи и последующей раздачи
  • раздавать потоки peer-to-peer
  • играть поток другого пользователя и отправлять свой (видеочат)
  • конвертировать сервером другие протоколы, например RTMP, RTSP, и т.д., и играть в браузере как WebRTC

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


//Запуск трансляции из браузера на сервер

session.createStream({name:”mystream”}).publish();

//Воспроизведение трансляции браузером

session.createStream({name:”mystream”}).play();

HLS работает там, где не работает WebRTC


WebRTC работает в последних версиях браузеров, однако имеют место два следующих фактора: 1) Не все пользователи своевременно обновляют браузеры и вполне могут сидеть на каком-нибудь Хроме трехлетней выдержки. 2) Чуть ли не раз в неделю выходят обновления и появляются новые браузеры, WebView, а также другие клиенты и мессенджеры, умеющие серфить интернет. Стоит ли говорить, что далеко не во всех из них присутствует поддержка WebRTC, а если и присутствует, то может быть довольно урезанной. Смотрите как дела обстоят сейчас:



Отдельная головная боль — всеми любимые яблочные устройства. Они относительно недавно получили поддержку WebRTC и, порой удивляют особенностями поведения по сравнению с православными webkit-браузерами. И там где не работает или не очень хорошо работает WebRTC, отлично работает HLS. В связи с этим, требуется совместимость, и что-то вроде конвертера, который позволит преобразовать WebRTC в HLS и проиграть его практически на любом устройстве.


Изначально HLS не был задуман для потоков реального времени. Действительно, какой может быть видеореалтайм по HTTP? Задача HLS — нарезать видео на кусочки и ровно, не торопясь, доставить их до плеера, путем скачивания одного за другим. HLS плеер ожидает строго сформированного и ровного видеопотока. И здесь возникает конфликт, так как WebRTC напротив, может позволить себе терять пакеты из-за требований реалтайма и низкой задержки и иметь плавающий FPS / GOP и непостоянный битрейт — быть полной противоположностью HLS в плане предсказуемости и размеренности потока.


Очевидный подход — депакетизация WebRTC (SRTP) и последующая конвертация в HLS, может не работать в нативном HLS плеере Apple или работать в непригодном для продакшена виде с фризами. Под нативным плеером здесь понимается плеер, который используется в яблочных iOS Safari, Mac OS Safari, Apple TV.


Поэтому, если вы заметили фриз HLS в нативном плеере, возможно это оно, и источником стрима является WebRTC или другой динамический стрим с неровной разметкой. Кроме этого, в реализации нативных Apple плееров встречается поведение, которое можно понять только опытным путем. Например, сервер должен начать отправку HLS сегментов немедленно, сразу после отдачи m3u8 плейлиста. Промедление в секунду грозит фризом. Если в процессе поменялся конфиг битстрима (что довольно частое явление при WebRTC стриминге), также будет фриз.


Борьба с фризами в нативных плеерах


Таким образом, прямая и честная депакетизация WebRTC и пакетизация в HLS в общем случае не работает. В сервере потокового видео Web Call Server (WCS) мы решаем проблему двумя способами, а третий предлагаем в качестве альтернативы:


1) Транскодирование.


Это наиболее надежный способ, позволяющий выровнять WebRTC поток под требования HLS, выставить нужный GOP, FPS, и т.д. Однако в некоторых случаях транскодирование не является хорошим решением, например транскодирование 4к потоков VR видео — так себе идея. Такие тяжелые потоки транскодировать очень дорого в плане процессорного времени или ресурсов GPU.



2) Адаптации и выравнивание WebRTC потока на лету под требования HLS.


Это специальные парсеры, которые анализируют H.264 битстрим и корректирует его под особенности / баги нативных HLS плееров Apple. Здесь надо признать, что ненативные плееры вроде video.js и hls.js более толерантны к потокам с динамическим битрейтом и FPS коим является WebRTC и не тормозят там, где эталонная по сути реализация Apple HLS встает в вечный фриз.



3) Использовать в качестве источника потока RTMP вместо WebRTC.


Несмотря на то, что флэш отошел от дел, RTMP протокол активно используется для стриминга, взять тот же OBS Studio. И надо признать, что RTMP энкодеры производят в целом более ровные потоки чем WebRTC и поэтому практически не дают фризов в HLS, т.е. Конвертация RTMP > HLS с точки зрения фризов выглядит гораздо более годной в том числе и в нативных HLS плеерах. Поэтому если стриминг осуществляется с десктопа и OBS, то для конвертации в HLS лучше использовать его. Если же источником является Chrome браузер, то RTMP уже воспользоваться не получится без установки плагинов, и здесь только WebRTC.



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


WebRTC в HLS на CDN


Отдельные неприятности могут поджидать в распределенной системе, когда между источником WebRTC потока и HLS плеером находится несколько серверов доставки WebRTC стримов, а именно CDN, в нашем случае на базе WCS сервера. Выглядит это так: есть Origin — сервер, который принимает WebRTC поток, есть Edge — серверы, которые раздают этот поток в том числе и по HLS. Серверов может быть много, что обеспечивает возможность горизонтального масштабирования системы. Например, к одному Origin — серверу можно подключить 1000 HLS серверов, в этом случае емкость системы масштабируется в 1000 раз.



Проблема уже была обозначена немного выше, и возникает эта проблема как правило в нативных плеерах: iOS Safari, Mac OS Safari, Apple TV. Под нативным имеется в виду плеер, который работает с прямым указанием урла плей листа в теге, например <video src="https://host/test.m3u8"/>. Как только плеер запросил плей-лист, а это действие является фактически первым шагом воспроизведения HLS потока, сервер обязан сразу, без какой-либо задержки, начать отдавать сегменты HLS видео. Если сервер не начинает отдавать сегменты немедленно, плеер решает что его обманули и останавливает воспроизведение. Опять же, такое поведение характерно именно для нативных HLS плееров Apple, но мы не можем сказать пользователям — “не используйте пожалуйста iPhone Mac и Apple TV для воспроизведения HLS потоков”, пользователи не поймут.


Итак, при попытке проиграть HLS стрим на Edge сервере, сервер должен немедленно начать отдачу сегментов, но как он это сделает если по факту стрима у него нет? Действительно, при попытке воспроизведения стрим на этом сервере отсутствует. Логика CDN работает по принципу Lazy Loading — мы не погоним стрим на сервер до тех пор, пока кто-то этот стрим на этом сервере не запросит. Возникает проблема первого подключившегося — первый, кто запросил HLS поток с Edge — сервера и имел неосторожность сделать это с нативного плеера Apple, получит фриз по той причине, что должно пройти какое-то время для того чтобы заказать этот стрим с Origin сервера, получить его на Edge и приступить к HLS нарезке. Даже если это займет три секунды, плеер это не спасет. Он уйдет в фриз.



Здесь снова вырисовываются два решения: одно нормальное, другое — не очень. Можно было бы отказаться от подхода Lazy Loading в CDN и рассылать трафик всем узлам вне зависимости от того, есть там зрители или нет. Решение, возможно пригодное для тех, кто не ограничен в трафике и вычислительных ресурсах. Origin будет гнать трафик на все Edge серверы, в результате все серверы и сеть между ними будут постоянно загружены. Пожалуй эта схема подошла бы только для каких-то специфических решений с малым количеством входящих потоков. При тиражировании большого количества потоков такая схема будет явно неэффективна по ресурсам. И если вспомнить, что мы решаем всего лишь “проблему первого подключившегося из нативного браузера”, то понятно, что оно того не стоит.



Второй вариант более элегантный, но тоже обходной. Мы отдаем первому подключившемуся пользователю видео картинку, но это пока еще не тот стрим, который он желает увидеть — это прелоадер. Так как мы что-то должны отдать уже сейчас и сделать это немедленно, а исходного стрима у нас нет (он еще заказывается и доставляется с Origin-а), мы принимаем решение попросить клиента немного подождать и показать ему видео прелоадера с двигающейся анимацией. Пользователь ждет несколько секунд, прелоадер крутится, и когда доходит реальный стрим, пользователю начинается показ реального стрима. В результате первый пользователь увидел прелоадер, а последующие подключившиеся наконец-то увидели нормальный HLS стрим, пришедший из CDN, работающей по принципу Lazy Loading. Инженерная проблема решена.


Но не до конца


Казалось бы, все работает здорово. CDN функционирует, HLS потоки забираются с краевых серверов Edge и решена проблема первого подключившегося. И здесь появляется еще один подводный камень — мы отдаем прелоадер в фиксированном соотношении сторон 16:9, а в CDN могут входить потоки любых форматов: 16:9, 4:3, 2:1 (VR видео). И это является проблемой, потому что если отдать плееру прелоадер в формате 16:9, а заказанный стрим окажется в формате 4:3, то нативный плеер снова ждет фриз.


Поэтому встает новая задача — требуется знать с каким именно соотношением сторон поток входит в CDN и отдавать прелоадер в том же соотношении. Особенностью WebRTC потоков является сохранение соотношения сторон при изменении разрешения и при транскодировании — если браузер решает понизить разрешение, он понижает его в том же соотношении. Если сервер решает транскодировать поток, он сохраняет соотношение сторон в той же пропорции. Поэтому логично, что если мы хотим показать прелоадер для HLS, мы показываем его в том же соотношении сторон, в котором заходит стрим.



CDN работает следующим образом: когда на Origin-сервер заходит трафик, он сообщает остальным серверам в сети, в том числе Edge-серверам о новом потоке. Проблема в том, что в этот момент разрешение исходного потока может быть еще не известно. Разрешение несут конфиги H.264 битстрима вместе с ключевым фреймом. Поэтому может случиться так, что Edge сервер получит информацию что стрим есть, но не будет знать о его разрешении и соотношении сторон, что не позволит ему корректно сгенерировать прелоадер. В связи с этим необходимо сигнализировать о наличии стрима в CDN только при наличии ключевого фрейма — это гарантированно даст Edge-серверу информацию о размерах и позволит сгенерировать корректный прелоадер чтобы предотвратить “проблему первого подключившегося зрителя”.



Итоги


Конвертация WebRTC в HLS в общем случае дает фризы при воспроизведении в нативных плеерах Apple. Проблема решаема анализом и корректировкой битстрима H.264 под требования HLS от Apple либо транскодирования, либо с помощью миграции на RTMP протокол и энкодер в качестве источника потока. В распределенной сети с ленивой загрузкой потоков существует проблема первого подключившегося зрителя, которая решается с помощью прелоадера и определения разрешения на стороне Origin сервера — точки входа потока в CDN.


Ссылки


Web Call Server — WebRTC сервер


CDN для стриминга WebRTC с низкой задержкой — CDN на базе WCS


Воспроизведение WebRTC и RTMP видеопотоков по HLS — Функции сервера по конвертации потоков из различных источников в HLS

Tags:
Hubs:
+6
Comments 21
Comments Comments 21

Articles

Information

Website
flashphoner.com
Registered
Founded
2010
Employees
2–10 employees
Location
Россия