В данной статье мы расскажем о задержке, возникающей при проведении онлайн-трансляций с веб-камеры из браузера. Почему она возникает, как ее избежать и как сделать онлайн-трансляцию действительно трансляцией в реальном времени.
Далее мы покажем что происходит с задержкой на примере реализации WebRTC и как при использовании WebRTC удается сохранить задержку на низком уровне, пригодном для комфортного общения.
Задержка
1-3 секунды уже вызывает легкий дискомфорт в общении. Лаг уже явно заметен и под него приходится подстраиваться. Зная, что лаг есть, вы говорите как в рацию, и ждете пока долетит до удаленной стороны и придет ответ. Такую задержку еще можно назвать Роман-Татьяна, в честь двух журналистов, которые выходят на видеосвязь с места событий.
— Роман, вы меня слышите? Роман?
— Прошло 3 секунды
— Слышу вас отлично. Татьяна?
— Прошло 3 секунды…
— Роман, тут пии… что творится. Роман?
Распространенные мифы о задержке
Ниже приведем три часто-встречаемых заблуждения, связанных с задержкой и качеством передачи видео в Web.
У меня 100 mbps
1. У меня 100 мегабит, никаких проблем быть не должно.
На самом деле не важно, сколько мегабит нарисовал провайдер в своем рекламном буклете. Важна реальная пропускная способность между вашим устройством и удаленным сервером или устройством, включая все узлы, через которые проходит трафик. Провайдер физически не может организовать 100 Mbps до любого произвольного узла в сети Интернет. Даже скорость 1 Mbps не гарантирована. Допустим ваш собеседник физически находится в глухой бразильской провинции, а вы вещаете из московского дата центра. Сколь бы толстым и быстрым ни был ваш заявленный канал, он не будет таким же до конечного адресата.
Таким образом, складывается такой неприятный факт — на самом деле вы не знаете, какова действительная пропускная способность канала между вами и вашим собеседником, и об этом не знает даже ваш провайдер, поскольку это плавающая величина в каждый момент времени и для каждого конкретного хоста, с которым вы обмениваетесь информацией.
Кроме пропускной способности, важна еще и регулярность прихода пакетов (no-jittering). Вы можете качать видео с торрентов на больших скоростях или видеть хорошие результаты в сервисе Speedtest, но при воспроизведении в реальном времени и с минимальной задержкой — важно получить все пакеты вовремя.
Было бы идеально, если бы пакеты приходили именно тогда, когда требуется их декодинг и отображение на экране — миллисекунда в миллисекунду. Но сеть не идеальна и в ней существует jitter. Пакеты приходят нерегулярно — то запаздывают, то приходят пачками, что требует их динамической буферизации для плавного воспроизведения. Если много сбрасывать, то ухудшается качество. Если много буферизировать, то вырастет задержка.
Поэтому, если кто-то говорит, что у него хорошая и быстрая сеть (в контексте передачи real-time видео) — не верьте. Любой узел сети в любой момент времени может применить ограничения и начать сбрасывать или задерживать в своих очередях видео пакеты и с этим ничего нельзя сделать. Нельзя отправить сообщение всем узлам на пути пакета и сказать “Эй, не сбрасывайте мои пакеты. Мне нужна минимальная задержка”. Точнее сделать это можно, промаркировав пакеты специальным образом. Но не факт, что узлы сети, через которые пойдет этот пакет, ее применят.
У меня LAN
2. У меня в локальной сети точно задержки быть не должно.
В локальной сети задержка действительно менее вероятна, просто потому, что трафик проходит меньше узлов — как минимум три: устройство отправителя, роутер, устройство получателя видео.
Эти три устройства имеют свои операционные системы, буферы, сетевые стеки. Что будет если например устройство отправителя активно раздает торренты? Или если сетевой стек сервера или CPU нагружены другими задачами или если в офисе беспроводная сеть и несколько сотрудников одновременно смотрят YouTube в разрешении 720p?
При достаточно толстом (с высоким битрейтом) видеопотоке, около 10 Mbps, роутер или другой узел вполне может начать сбрасывать или задерживать часть пакетов.
Таким образом, задержка в локальной сети также вероятна и зависит от битрейта видеопотока и возможностей узлов этой сети по обработке данных.
Здесь стоит заметить, что подобные проблемы в среднестатистической локальной сети встречаются на порядок реже чем в глобальной и чаще всего их причинами являются перегрузки в сети или проблемы с железом / софтом.
В итоге мы утверждаем, что любая сеть, включая локальную, не идеальна и в ней могут задерживаться и сбрасываться видео пакеты и мы, в общем случае, не можем прямо повлиять на интенсивность и количество сброшенных пакетов.
У меня UDP
3. Я использую UDP, а при использовании UDP протокола не бывает задержек.
Пакеты отправленные по UDP могут быть точно также задержаны или потеряны в узлах сети и если этих пакетов не хватает для сборки видео и декодирования, они могут быть запрошены приложением повторно, что вызовет задержку в воспроизведении.
Протоколы
В то время, как космические корабли бороздят, мы осознаем, что веб-протоколов передачи данных (протоколов, с которыми работают браузеры) у нас всего два: TCP и UDP.
TCP — протокол с гарантированной доставкой. Это означает, что отправка пакета в сеть — это необратимая операция. Если вы отправили данные в сеть, они будут там путешествовать пока не дойдут до места назначения или пока TCP соединение не будет разорвано по таймауту. И это основная причина задержки при использовании протокола TCP.
Действительно, если пакет был задержан или сброшен, он будет высылаться вновь и вновь, пока удаленная сторона не пришлет подтверждение о прибытии пакета и это подтверждение не дойдет до отправителя.
На протоколе TCP основываются следующие протоколы / технологии более высокого уровня, которые используются для передачи Live-видео в web:
- RTSP (interleaved mode)
- RTMP
- HTTP (HLS)
- WebRTC over TLS
- DASH
Все эти протоколы гарантируют высокую задержку при проблемах в сети. При этом важно отметить, что эти проблемы вполне могут быть не на отправителе или принимающей стороне, а на любом из промежуточных узлов. Поэтому при попытке определить причину такой задержки, зачастую бесполезно проверять сеть отправителя видеопотока и сеть принимающей стороны. С ними может быть все хорошо и при этом будет задержка более 5 секунд, вызванная чем-то посередине.
Учитывая вышеизложенное утверждение: любая сеть не идеальна и в ней могут задерживаться и сбрасываться видео пакеты, делаем вывод о том, что с целью получения гарантированной минимальной задержки нужно полностью отказаться от использования протоколов, основанных на TCP.
Отказаться от TCP не всегда просто, т.к. В отдельных случаях ему нет альтернатив. Например, если на корпоративном Firewall закрыты все порты кроме 443 (https), то единственный способ передать видео, это туннелировать его в https, организовав передачу видео пакетов по HTTPS протоколу, основанному на TCP. В этом случае придется мириться с непредсказуемой задержкой, но видео так или иначе будет доставлено.
UDP — это протокол с негарантированной доставкой пакетов. Это означает, что если вы отправили пакет в сеть, он может быть потерян или задержан, но это не помешает вам отправлять другие пакеты следом, а получателю их принимать и обрабатывать.
Плюсом этого подхода является тот факт, что пакет не будет высылаться снова и снова, как в случае с TCP, ожидая гарантированного подтверждения от получателя на уровне протокола. Получатель сам решает — ждать ли ему, когда все пакеты соберутся в нужном порядке или работать с тем что есть. В TCP такой свободы у получателя нет.
На UDP не так много веб-протоколов для передачи видео:
- WebRTC over UDP
- RTMFP
Здесь под WebRTC мы имеем в виду весь UDP-based стек протоколов этой технологии STUN, ICE, DTLS, SRTP, который работает по UDP и обеспечивает в конечном итоге доставку видео по SRTP.
Таким образом, используя UDP мы имеем возможность доставить пакеты быстро, с частичными потерями. Например потерять или надолго задержать 5% отправленных пакетов. Преимущество состоит в том, что мы сами, на уровне приложения можем решать — достаточно ли нам 95% полученных вовремя пакетов для корректного отображения видео и при желании запросить недостающие пакеты опять же на уровне приложения и запросить их столько раз, сколько потребуется для того чтобы а) достичь необходимого качества видео б) сохранить задержку на низком приемлемом уровне.
В результате UDP протокол не освобождает нас от необходимости досылки пакетов, но позволяет реализовать досылку более гибко, балансируя между качеством видео, зависящим от потери пакетов и задержкой. Этого нельзя сделать в TCP, т.к. там по дизайну вшита гарантированная доставка.
Управление перегрузками (Congestion control)
До этого мы сформировали утверждение о том, что любая сеть не идеальна и в ней могут задерживаться и сбрасываться видео пакеты и мы не можем на это повлиять.
Обзор протоколов так и не дал счастливого смайла справа, но мы пришли к выводу, что для низкой задержки нужно использовать протокол UDP и реализовать досылку пакетов, балансируя качество видео.
В самом деле, если наши пакеты сбрасываются или задерживаются в сети, возможно мы слишком много отправляем их в единицу времени. И если мы не можем дать команду, дающую нашим пакетам высокий приоритет, то мы можем уменьшить нагрузку на эти промежуточные узлы, отправляя им меньше трафика.
Таким образом, абстрактное приложение для low-latency стриминга имеет две основные цели:
- Задержка менее 500 мс
- Максимально возможное качество для данной задержки
И эти цели достигаются следующими способами:
Цели | |
Задержка менее 500 мс | Максимально возможное качество |
|
|
В левом столбце перечислены способы, направленные на уменьшение задержки, а в правой — вещи, способствующие улучшению качества видео.
Таким образом, видеопоток, в котором требуется низкая задержка и качество не является статическим и постоянным и должен гибко реагировать на изменения сети во времени, в нужное время запрашивая досылку пакетов, понижая или повышая битрейт в зависимости от состояния сети в текущий момент времени.
Приведем один из распространенных вопросов: “Могу ли я стримить 720p live видео с задержкой 500 мс”. Этот вопрос, в общем случае не имеет смысла, т.к. разрешение 1280x720p при битрейте 2 Mbps и при битрейте 0.5 Mbps — это две совершенно разных картинки, хотя обе имеют разрешение 720p, одна будет четкой, а вторая сильно разбавленной макроблоками.
Правильным был бы вопрос: «Могу ли я стримить качественное видео 720p с задержкой менее 500 мс и битрейтом 2 Mbps». Ответ — да можете, если между вами и точкой назначения есть реальная выделенная 2 Mbps полоса (не та полоса, которую указывает провайдер), которая позволяет это сделать. Если такой гарантированной полосы нет, то битрейт и качество картинки будет плавающим, с целью вписаться в указанную задержку, с ежесекундной подстройкой под существующую полосу.
Как видите, смайл улыбается, но задает себе вопрос “Am I happy?”. Действительно, плавающий битрейт, адаптация разрешения под полосу пропускания и частичная досылка пакетов — это компромиссы, которые не позволяют одновременно достичь близкой к нулевой задержки и true Full HD качества в произвольной сети. Но такой подход позволяет держать качество приближенным к максимальному в каждый момент времени и контролировать задержку, удерживая ее на низком уровне.
WebRTC
Многие ругают технологию WebRTC за якобы сырость и избыточность. Однако если копнуть глубже в особенности реализации, то выясняется, что технология вполне пригодная и делает свое дело неплохо — т.е. обеспечивает доставку аудио и видео в реальном времени с сохранением низкой задержки.
Выше мы писали, что в силу неоднородности сети, для поддержания низкой задержки приходится постоянно корректировать параметры потока, такие как битрейт, FPS и разрешение. Всю эту работу хорошо видно в обычном Chrome браузере, во вкладке chrome://webrtc-internals
Все начинается с веб-камеры. Предположим камера хорошая и выдает видео стабильно 30 FPS. При этом вот что может происходить с реальным видеопотоком:
Как видно из графика, несмотря на то что камера выдает 30 FPS, реальный фрейм рейт скачет при передаче в примерном диапазоне 25-31 FPS и на локальных минимумах может достигать 21-22 FPS.
Одновременно с FPS снижается битрейт. Действительно, чем меньше видео кодируется, тем меньше фреймов / пакетов отправляется в сеть, тем меньше общая скорость видеопотока.
К вспомогательным метрикам относятся RTT, NACK и PLI, которые влияют на поведение браузера (WebRTC) и результирующий битрейт и качество потока.
RTT — это Round Trip Time, который говорит о “пинге” до получателя.
NACK — это сообщение о потерянных пакетах, отправленное получателем потока, отправителю этого потока.
PLI — это сообщение о потерянном ключевом кадре с требованием его досылки.
Основываясь на количестве потерянных пакетов, досылок, RTT, можно строить выводы о качестве сети в каждый момент времени и динамически корректировать мощность видеопотока таким образом, чтобы она не превышала пределов возможности каждой конкретной сети и не забивала канал. В WebRTC это уже реализовано и работает.
Тестирование 720p WebRTC видеопотока
Для начала протестируем трансляцию WebRTC видеопотока в разрешении 1280x720 (720p) и измерим задержку. Тестировать трансляцию будем через WebRTC медиасервер Web Call Server 5. Тестовый сервер находится на площадке Digitalocean в датацентре Франкфурта. Пинг до сервера составляет 90 ms. Интернет-провайдером обозначена скорость 50 Mbps.
Параметры тестирования:
Сервер | Web Call Server 5, DO, Frankfurt DC, ping 90ms, 2 core, 2Gb RAM |
Разрешение потока | 1280x720 |
Система | Windows 8.1, Chrome 58 |
Тест | Эхо тест с отправкой видео на сервер в одном окне браузера и получением в другом |
Для тестирования мы использовали стандартный пример media_devices.html, расположенный по этой ссылке. Исходный код примера находится здесь.
Для того, чтобы установить разрешение потока 720p, выбираем камеру и выставляем 1280x720 в настройках Size. Кроме этого Play / Video / Quality выставляем в 0 чтобы не использовать транскодинг.
Таким образом, мы отправляем на удаленный сервер 720p видеопоток и проигрываем его в окне справа. На странице в это время отображается подтверждающий статус PUBLISHING.
Далее запускаем таймер с миллисекундами с виртуальной камеры и делаем несколько скриншотов для измерения реальной задержки.
Скрин 1
Скрин 2
Скрин 3
Скрин 4
Скрин 5
Скрин 6
Скрин 7
Скрин 8
Скрин 9
Скрин 10
Результаты теста и задержка
В результате, получаем следующую таблицу с результатами измерений в миллисекундах:
Captured | Displayed | Latency | |
1 | 09599 | 09277 | 322 |
2 | 13376 | 12992 | 384 |
3 | 16256 | 15866 | 390 |
4 | 19198 | 18751 | 447 |
5 | 22394 | 22022 | 372 |
6 | 25661 | 25211 | 450 |
7 | 32511 | 32126 | 385 |
8 | 36166 | 35839 | 327 |
9 | 40318 | 39935 | 383 |
10 | 45310 | 44987 | 323 |
Получаем такой график задержки 720p потока на нашем минутном тесте:
Тест 720p трансляции показал довольно неплохой результат с визуальной задержкой в 300-450 миллисекунд.
Графики WebRTC битрейта
Посмотрим, что происходит в этот момент с видеопотоком на уровне WebRTC. Для этого вместо таймера запустим мультфильм в высоком разрешении чтобы посмотреть, как WebRTC будет управлять высоким битрейтом.
Ниже приведены графики этой WebRTC-трансляции
Из графиков видно, что битрейт потока динамически меняется в диапазоне 1-2 Mbps. Это происходит потому, что сервер автоматически определяет нехватку канала и просит Chrome понижать битрейт время от времени. Планка битрейта меняется динамически и обозначена на графике красным цветом googAvailableSendBandwidth. Зеленым цветом обозначен реально отправляемый битрейт googTransmitBitrate.
Так работает Congestion Control на стороне сервера. С целью избежать перегрузок сети и потерь пакетов, сервер постоянно корректирует битрейт и браузер следует командам сервера по корректировке битрейта.
При этом на графиках ширины и высоты все стабильно. Отправляемая ширина 1280, а высота 720p. Т.е. Отправляемое разрешение не меняется и управление битрейтом происходит без изменения разрешения, путем понижением битрейта кодирования видео.
Контроль перегрузок CPU
Для того, чтобы разрешение не менялось, мы отключили на тестах использование CPU детектора (googCpuOveruseDetection) для браузера Google Chrome.
CPU детектор отслеживает загрузку процессора и при достижении некоторого порога инициирует события, которые приводят к сбросу разрешения браузером Chrome. Отключив эту функцию мы позволили перерасход процессора, и зафиксировали разрешение.
mediaConnectionConstraints: {"mandatory": {googCpuOveruseDetection: false}}
С использованием CPU-детектора графики выглядят плавнее, но при этом разрешение видеопотока постоянно переключается вверх и вниз.
Для тестирования адаптаций выберем машину послабее. Это будет Mac Mini 2011 года с core i5 1.7 Ghz и Chrome 58. В качестве теста будем использовать тот же самый мультик.
Обратите внимание, что в самом начале стриминга разрешение видео упало на 540x480.
В результате имеем следующие графики:
На графиках можно видеть как меняется ширина и высота картинки, т.е. Разрешение видео:
А на этих графиках показано, на фоне каких изменений проходили понижения и повышения ширины и высоты.
Параметр googAdaptationChanges показывает количество событий (адаптаций), которые инициировал Chrome в процессе стриминга. Чем больше адаптаций проходит, тем чаще меняется разрешение видео и битрейт в процессе видео стриминга.
Что касается битрейта, его график получился более пилообразным, несмотря на то, что сервер не занижал верхнюю планку.
Такое агрессивное изменение битрейта связано с двумя вещами:
- Включением адаптаций googAdaptationChanges на стороне браузера Google Chrome, которые были вызваны повышенной нагрузкой на CPU.
- Использованием кодека H.264, который кодирует иначе чем VP8 и может сильно сбрасывать битрейт кодирования в зависимости от содержания сцены.
Заключение
В результате мы сделали следующее:
- Измерили задержку WebRTC трансляции через удаленный сервер и определили ее средние значения.
- Показали как ведет себя битрейт 720p видеопотока в VP8 кодеке при отключенных адаптациях CPU, как битрейт адаптируется к сетевым условиям.
- Увидели планку битрейта, динамически выставляемую сервером.
- Протестировали WebRTC-трансляцию на менее мощной клиентской машине с адаптацией под CPU и H.264 кодеком и увидели динамическую корректировку разрешений видеопотока.
- Показали метрики CPU, которые влияют на изменение разрешения и количество адаптаций.
Таким образом, можно ответить на несколько вопросов, которые подразумевались в начале статьи:
Вопрос: Что нужно сделать, чтобы сделать WebRTC трансляцию с минимальной задержкой?
Ответ: Просто сделать трансляцию. Разрешение и битрейт потока автоматически скорректируются к значениям, обеспечивающим минимальную задержку. Например, если задать 1280x720, битрейт может спуститься до 1 Mbps, а разрешение 950x540.
Вопрос: Что нужно сделать, чтобы сделать WebRTC трансляцию с минимальной задержкой в стабильном разрешении 720p?
Ответ: Для этого канал пользователя должен реально давать как минимум 1 Mbps и должны быть отключены адаптации CPU. В этом случае разрешение не будет падать и подстройка будет происходить только за счет битрейта.
Вопрос: Что будет с 720p видеопотоком на полосе 200 kbps?
Ответ: Будет размытая макроблоками картинка и низкий (около 10) FPS. При этом задержка останется низкой, но качество видео визуально будет очень плохим.
Ссылки
Media Devices — тестовый пример WebRTC трансляции, который использовался для тестирования задержки
Source — исходный код примера тестовой трансляции.
Web Call Server — WebRTC сервер
chrome://webrtc-internals — графики WebRTC
Автор: flashphoner