Типичный запрос в техподдержку Voximplant: «Почему видеозвонок между двумя Chrome выглядит лучше, чем видеозвонок между MS Edge и нативным iOS-приложением»? Коллеги обычно отвечают нейтральное — «потому что кодеки». Но мы, айтишники, любопытны. Даже если я не разрабатываю новый Skype-for-web, чтение «какой браузер что может» и как они разбивают одно видео на несколько потоков разного качества обогащает картину мира и дает свежую тему для обсуждения в курилке. Удачно подвернувшаяся статья от широко известного в узких кругах Dr Alex (с лучшим объяснением термина «media engine» из всех, что я видел), немного нашего опыта, пара вечеров в «Циферблате» — и адаптированный для Хабра перевод ждет под катом!
Кодеки и ширина канала
Когда говорят о видеокодеках, чаще всего обсуждают баланс качества и ширины используемого канала. И любят игнорировать вопросы нагрузки на процессоры и того, как технически передавать видео. Довольно разумно, если мы обсуждаем кодирование уже записанного видео.
Ведь если у вас есть готовое видео, то нет особой разницы, будет оно сжиматься пару минут, пару часов или даже пару дней. Любые затраты процессора и памяти будут оправданы, ведь это одноразовое вложение, а видео потом можно раздать миллионам пользователей. Лучшие видеокодеки сжимают видео в несколько проходов:
- Проход №1: Видео разбивается на части с общими признаками: действие происходит на одном и том же фоне, быстрая или медленная сцена и тому подобное.
- Проход №2: Сбор статистики для кодирования и информации о том, как кадры меняются во времени (чтобы такую информацию получить, нужно несколько кадров).
- Проход №3: Каждая часть кодируется со своими настройками кодека и с использованием информации, полученной на втором шаге.
Стриминг — совсем другое дело. Никто не будет ждать окончания подкаста, стрима или шоу прежде чем начать кодировать видео. Кодировать и отправлять нужно сразу же. Прямой эфир на то он и прямой, что минимальная задержка становится важнее всего.
При использовании физических носителей, DVD или Blu-Ray дисков, размер видео фиксирован и перед кодеком стоит задача обеспечить максимальное качество для заданного размера. Если видео раздается по сети, то задача кодека подготовить такой(-ие) файл(-ы), чтобы получилось максимальное качество при фиксированной ширине канала или же минимальная ширина канала при фиксированном качестве, если нужно уменьшить цену. Задержки сети при этом можно игнорировать и буферизировать на стороне клиента столько секунд видео, сколько нужно. А вот для стриминга ни размер, ни качество фиксировать нет особой необходимости, у кодека другая задача: любой ценой уменьшить задержки.
Наконец, создатели кодеков долгое время держали в уме только один сценарий использования: на компьютере пользователя проигрывается одно и только одно видео. Которое, к тому же, почти всегда можно декодировать силами видеочипа. Потом случились мобильные платформы. А затем WebRTC, для обеспечения минимальных задержек которой разработчики очень хотели использовать Selective Forwarding Unit серверы.
Использование кодеков при видеозвонках настолько отличается от традиционного использования при проигрывании видео, что сравнивать кодеки «в лоб» становится бессмысленно. Когда на заре WebRTC сравнивали VP8 и H.264, одна из самых жарких дискуссий шла относительно настроек кодеков: делать их «реалистичными» с учетом ненадежных сетей или же «идеальными» для максимального качества видео. Борцы за «чистоту сравнения кодеков» на полном серьезе утверждали, что кодеки надо сравнивать без учета потерь пакетов, джиттера и других сетевых проблем.
Что сейчас с кодеками?
- H.264 и VP8 примерно одинаковые по соотношению качества видео и используемой ширины канала;
- H.265 и VP9 также примерно соответствуют друг другу, показывая в среднем на 30% лучшие результаты по сравнению с кодеками предыдущего поколения за счет увеличившейся на 20% загрузки процессора;
- новый кодек AV1, гремучая смесь VP10, daala и thor, лучше кодеков предыдущего поколения примерно настолько же, насколько те лучше своих предшественников.
А теперь сюрприз: всем плевать на эти различия, когда речь заходит о видеозвонках и видеоконференциях. Самым главным становится то, как кодек играет в команде с остальной инфраструктурой. Разработчиков заботит то, что называется новым термином media engine: как браузер или мобильное приложение захватывает видео, кодирует/декодирует его, разбивает на RTP-пакеты и борется с сетевыми проблемами (помните видео из нашей предыдущей хабрастатьи? Так вот, там сравнивали как раз media engine — примечание переводчика). Если энкодер не сможет работать при резком уменьшении ширины канала или стабильно поддерживать 20 кадров в секунду, если декодер не сможет работать при потере одного сетевого пакета, то какая разница, насколько хорошо кодек сжимает видео? Нетрудно понять, почему Google спонсирует исследования команды из Стенфорда по лучшему взаимодействию между кодеком и сетью. Это будущее видеокоммуникаций.
Кодеки и media engine: все сложно
У видеозвонков и видеоконференций почти те же задачи, что у обычных медиа. Только вот приоритеты совсем другие:
- Нужно 30 кадров в секунду (скорость кодека).
- Нужно 30 кадров в секунду с интерактивностью (минимальные задержки).
Еще у нас есть интернет между участниками, о качестве которого мы можем только догадываться. Обычно все еще хуже. Поэтому:
- Нужно хорошо переживать небольшие изменения ширины канала, когда в коворкинг приходит еще один посетитель.
- Нужно хоть как-то переживать сильные изменения ширины канала, когда этот посетитель начинает качать торренты.
- Нужно хорошо переживать джиттер (случайные задержки между получаемыми пакетами, благодаря которым они могут не просто задержаться, а прийти не в том порядке, в каком отсылались).
- Нужно переживать потерю пакетов.
3.1. Основные задачи media engine
Что значит «нужно 30 кадров в секунду»? Это значит, что у media engine есть 33 миллисекунды, чтобы захватить видео с камеры, звук с микрофона, сжать их кодеком, разбить на RTP-пакеты, обеспечить защиту передаваемых данных (SRTP = RTP + AES) и отправить по сети (UDP или TCP, в подавляющем большинстве случаев UDP). Все это на отправляющей стороне. А на принимающей стороне — повторить в обратном порядке. Так как кодирование обычно сложнее декодирования, то отправляющей стороне приходится тяжелее.
С технической стороны, цель «нужно 30 кадров в секунду» выполнима с задержками. И чем больше задержка, тем проще достигнуть цели: если на отправляющей стороне кодировать не по одному кадру, а сразу по несколько, то можно существенно сэкономить на ширине канала (кодеки лучше сжимают по несколько кадров за счет анализа изменений между всеми ними, а не только между текущим и предыдущими). При этом задержка между получением видеопотока с камеры и отправки по сети увеличивается пропорционально количеству буферизированных кадров плюс сжатие становится медленнее за счет дополнительных расчетов. Многие сайты используют этот трюк, декларируя время отклика между отправкой и получением сетевых пакетов между участниками видеозвонков. Задержку при кодировании и декодировании они умалчивают.
Чтобы видеозвонки были похожи на личное общение, создатели коммуникационных сервисов отказываются от всех настроек и профилей кодеков, которые могут создавать задержки. Получается такая деградация современных кодеков до покадрового сжатия. Поначалу такая ситуация вызывала неприятие и критику со стороны разработчиков кодеков. Но времена изменились, и сейчас у современных кодеков в дополнение к традиционным пресетам «минимальный размер» и «максимальное качество» добавился комплект настроек «реалтайм». А заодно и «шаринг экрана», тоже для видеозвонков (там своя специфика — большое разрешение, мало меняющаяся картинка, необходимость сжатия без потерь, иначе текст поплывет).
3.2. Media engine и публичные сети
Небольшие изменения ширины канала
Раньше кодеки не могли менять битрейт: при старте сжатия они принимали целевой битрейт в качестве настройки и далее выдавали фиксированное количество мегабайт видео в минуту. В те давние времена видеозвонки и видеоконференции были уделом локальных сетей и резервированной пропускной способности. А в случае проблем звали админа, который чинил резервирование ширины канала на циске.
Первым эволюционным изменением была технология «адаптивного битрейта». У кодека есть много настроек, которые влияют на битрейт: разрешение видео, незначительное уменьшение fps с 30 до 25 кадров в секунду, квантование видеосигнала. Последнее в этом списке — «огрубление» перехода между цветами, незначительные изменения которого мало заметны человеческому глазу. Чаще всего главной «настройкой» для адаптивного битрейта было именно квантование. А про ширину канала кодеку рассказывал media engine.
Большие изменения ширины канала
Механизм адаптивного битрейта помогает media engine продолжать транслировать видео при незначительных изменениях ширины канала. Но если ваш коллега начал качать торренты и доступный канал просел в два или в три раза, то адаптивный битрейт не поможет. Поможет уменьшение разрешения и фреймрейта. Последнее предпочтительнее, так как наши глаза менее чувствительны к количеству кадров в секунду, чем к разрешению видео. Обычно кодек начинает пропускать один или два кадра, снижая фреймрейт с 30 до 15 или даже до 10.
Важная деталь: media engine будет пропускать кадры на отправляющей стороне. Если у нас видеоконференция на несколько участников или вещание, а сетевая проблема не у отправителя, то одно «слабое звено» ухудшит качество видео для всех участников. В такой ситуации помогает связка simulcast (отправляющая сторона отдает сразу несколько видеопотоков разного качества) и SFU (Selective Forwarding Unit, сервер отдает каждому участнику видеоконференции или вещания поток нужного качества). У некоторых кодеков есть возможность создавать несколько simulcast потоков, SVC, которые дополняют друг друга: клиентам с самым слабым каналом отдается поток минимального качества, клиентам с более хорошим каналом отдается этот же поток плюс первый потом «апгрейда», клиентам с еще более хорошим каналом отдаются уже два потока «апгрейда» и так далее. Такой способ позволяет не передавать одни и те же данные в несколько потоков и экономит примерно 20% трафика по сравнению с кодированием нескольких полноценных видеопотоков. Еще он упрощает работу сервера — не нужно переключать потоки, достаточно не передавать клиентам пакеты с «апгрейдом». Тем не менее, любой кодек можно использовать для simulcast, это фича media engine и организации RTP-пакетов, а не кодека.
Джиттер и потери пакетов
С потерями труднее всего бороться. Джиттер чуть попроще — достаточно сделать на принимающей стороне буфер, в котором собирать опоздавшие и перепутанные пакеты. Не слишком большой буфер, иначе можно поломать реалтайм и стать буферизирующим видео ютубом.
С потерями пакетов обычно борются их повторной пересылкой (RTX). Если у отправителя хорошая связь с SFU, то сервер может запросить потерянный пакет, получить его повторно и все равно уложиться в 33 миллисекунды. Если же сетевое соединение ненадежно (более 0.01% потерь пакетов), то нужны сложные алгоритмы работы с потерями, например FEC.
Самое лучшее на данный момент решение это использовать SVC-кодеки. В таком случае для получения хоть какого-то видео нужны только «опорные» пакеты с потоком минимального качества, этих пакетов меньше, следовательно их проще отправить повторно, этого достаточно для «выживания» даже при очень плохой сети (более 1% потерь пакетов). Если Simulcast + SFU позволяют бороться с проседанием ширины канала, то Simulcast с помощью SVC кодека + SFU решает и вопросы ширины канала, и вопросы потерянных пакетов.
Что сейчас поддерживают браузеры
Firefox и Safari используют Media Engine от Google и время от времени обновляют libwebrtc. Делают они это сильно реже Хрома, новая версия которого выходит каждые 6 недель. Время от времени они начинают сильно отставать, но затем снова синхронизируются. За исключением поддержки кодека VP8 в Сафари. Даже не спрашивайте.
До ката таблица с полным сравнением кто что поддерживает, но в целом все довольно просто. Edge все обычно игнорируют. Выбор идет между поддержкой мобильной версии Safari и хорошим качеством видео. iOS Safari поддерживает только видеокодек H.264, в то время как libwebrtc позволяет использовать simulcast только с кодеками VP8 (разные потоки с разной частотой кадров) и VP9 (поддержка SVC). Но можно считерить и воспользоваться libwebrtc на iOS, создав нативное приложение. Тогда с simulcast все будет хорошо и пользователи получат максимально возможное качество видео при нестабильном интернет-подключении. Несколько примеров:
- Highfive — десктопное приложение на Electron (Chromium) с H.264 simulcast (libwebrtc) и звуковыми кодеками от Dolby;
- Attlasian — Интересное решение клиентом на React Native и libwebrtc для simulcast;
- Symphony — Electron для десктопа, React Native для мобильных устройства, и там и там поддерживается simulcast + дополнительные средства безопасности, совместимые с тем, чего хотят банки;
- Tokbox — VP8 с simulcast в мобильном SDK, используют патченную версию libvpx в libwebrtc.
Будущее
Уже сейчас понятно, что VP8 и VP9 в Safari не будет (в отличии от Edge, который VP8 поддерживает).
Несмотря на то, что Apple поддерживала включение H.265 в WebRTC, последние новости и ряд косвенных признаков указывают на AV1 как «next big thing». В отличии от остальной статьи, это мое личное мнение. Спека на передачу данных AV1 уже готова, но над кодеком пока ведутся работы. Сейчас reference implementation энкодера показывает печальные 0.3 кадра в секунду. Это не проблема при проигрывании заранее сжатого контента, но пока неприменимо для Realtime Communications. А пока вы можете попробовать проигрывание AV1 видео в Firefox, хотя это и не имеет отношения к RTC. Реализация от команды bitmovin, которая разработала MPEG-DASH и получила 30 миллионов инвестиций на создание инфраструктуры следующего поколения для работы с видео.
Автор: Grigory Petrov