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

О чём же речь? Речь о сокращении задержки между тем, когда что-то происходит перед камерой и тем, когда это дойдет до зрителя. Понятно, что трансляция лекции по квантовой физике будет доходить дольше, чем комеди-клаб, но мы всё же занимаемся техническими деталями.

Прежде чем переходить к обсуждению задержек (оно же latency, delay), надо ответить на очень важный вопрос: а зачем вообще их сокращать. Сокращать задержку хочется почти всегда, но требуется не всегда.

Так, например, прямой эфир с острополитическим ток-шоу в принципе стоит минуты на 3 придержать от прямого эфира, что бы можно было оперативно отреагировать на резкое развитие дискуссии, а вот вебинар или удаленное управление беспилотником требует минимальной задержки чтобы люди могли спокойно перебивать друг друга, а груз падал ровно в цель.

Прежде чем двигаться дальше, давайте зафиксируем один важный факт: уменьшение задержек при трансляции видео и аудио — это дорого, причем нелинейно дорого, поэтому в какой-то момент нужно остановиться на требуемой задержке, а для этого надо вернуться назад и понять: зачем нужно её сокращать.

Такое вступление не просто так. Мы проводили опрос среди наших клиентов, так выяснилось наличие противоречивого желания: те, кто занимаются вещанием телеканалов хотят стриминга с низкой задержкой. Зачем — никто не объяснил, просто хочется.

Формирование задержки


Давайте разберемся, как формируется задержка при передаче видео. Примерная схема доставки видео сигнала (схема видеотракта) следующая:

  1. с сенсора камеры снимается изображение в видеопамять
  2. видео энкодер кладет сырое изображение в буфер кодирования
  3. алгоритм сжатия видео находит оптимальный способ компрессии нескольких видео кадров в буфере
  4. сжатый видеокадр отправляется в серверный буфер доставки видео (если такой есть)
  5. видеокадр передается по UDP или копируется в ядерный буфер TCP для отправки
  6. байты долетают до клиента и складываются в ядерный буфер приемки сетевых данных
  7. доходят до клиента
  8. возможно складываются в буфер сортировки кадров
  9. оттуда складываются при необходимости в буфер компенсации флуктуации скорости сети
  10. из него отправляются в декодер, который накапливает свой буфер для b-frames
  11. декодер декодирует кадр и отправляет его на отрисовку

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

Почему? Да потому что буферизация — это обычный способ снизить стоимость, повысить общую пропускную способность. Есть ещё один момент: буфер помогает сгладить колебания. Колеблется скорость передачи по сети – не беда, закачаем на старте побольше байт/кадров и пока интернет восстанавливается, будем играть то, что лежит в буфере.

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

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

Детали


Снятие с сенсора


Вроде бы пустяк, но в старом добром аналоговом телевидении можно начинать проигрывать на телевизоре строчку ещё до того, как её закончили снимать (кстати, тут я утрирую, но будет интересно узнать, как оно на самом деле).

Но если разобраться, то можно понять, что сенсор сегодня — это 2 мегапикселя минимум, а то и больше. К нему приделан вовсе не Intel Xeon, а минимально справляющаяся железяка, которая на просто перекопирование данных тратит время.

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

Точную оценку по задержке здесь дать не готов.

Буфер кодирования


Энкодер занимается крайне ресурсоёмкой задачей, а так же жутко нагружает шину передачи данных между памятью и процессором. Ему надо перебрать разные комбинации вариантов сжатия видео, найти разницу между соседними кадрами и сделать кучу сложных математических вычислений. Учитывая, что FullHD видео на 25 кадрах в секунду — это порядка гигабита в секунду (100 мегабайт), нагрузка огромная. Но просьба не совершать классическую ошибку и не путать нагрузку на процессор с задержкой. Время, которое уходит на сжатие кадра всё равно меньше 1/fps (иначе уже можно не дергаться, всё равно ничего не получится), а задержку энкодер создает гораздо больше.

Дело в том, что энкодер накапливает в буфере несколько подряд идущих сырых кадров для того, что бы выдать как можно меньший битрейт с как можно большим качеством. Задачи для которых тут создается буфер такие:

  • поддержание среднеровного битрейта потока. Если в одном кадре очень хочется сделать качество получше, значит на остальных кадрах надо постараться поджаться
  • выбор оптимальных кадров, на которые можно ссылаться. Иногда так бывает, что стоит сослаться не на предыдущий кадр, а на следующий. Таким образом возникают перестановки кадров и экономия трафика до 15-20%

С этой задержкой можно играть, но прежде всего это будет приведет к росту битрейта. Есть хороший пост на покинувшем нас сайте от автора libx264 о low latency кодировании. Вот это оно.

Итого, здесь можно справиться за 1-2 кадра (по 40 мс каждый), а можно и потратить до 3-5 секунд, но сэкономить битрейт.

Помните, я вначале сказал, что за низкую задержку прийдется платить? Вот уже можно начинать платить битрейтом.

Буфер на сервере


Едва ли не самый частый вопрос нам про задержку: «у меня очень большая задержка при вещании через HLS, где у вас убрать буфер на сервере».

На самом деле серверная буферизация вполне бывает, например при упаковке mpegts очень хочется подождать с отправкой аудиокадров, что бы положить несколько кадров в один PES пакет. Или при упаковке таких протоколов, как HLS или DASH вообще надо ждать по несколько секунд.

Важный момент здесь: например в mpegts любят упаковывать в один PES кадр несколько аудиокадров. Теоретически можно открыть PES пакет, начать в него писать то что есть и слать это в сеть, потом послать видеокадр, а потом продолжить с другим видеокадром. Но здесь есть обычная проблема: в аудио PES кадре идет его длина, значит надо накопить аудио. Накопить, означает буфер, означает рост задержки.

Некоторые серверы буферизуют кадры даже при использовании покадровых протоколов типа RTMP для того, что бы сократить использование CPU, ведь послать один раз 100 килобайт дешевле, чем 2 раза по 50.

Т.е. здесь всё напрямую зависит от протокола: если у нас на сервере HLS или DASH, то буферизация хотя бы сегмента (1-10 секунд) неизбежна. Если покадровый протокол, то не нужно, смело можно рассылать кадры всем клиентам по одному, но всё равно так делают редко.

Если мы получаем откуда-то например RTP (с камер RTSP/RTP), то теоретически можем раздавать клиентам сразу RTP пакеты по их получению. Это даст чумовое снижение задержки меньше одного фрейма. На практике этот подход реализуется редко, потому что создает огромную сложность программирования и резко сужает вариативность использования софта. Чаще всего видеостриминговые серверы работают с кадрами, очищенными от контейнеров и протоколов.

Здесь есть маленькая деталь: существует инициатива CMAF low latency. Суть идеи в том, что когда приходит опорный кадр (он же keyframe), то сервер анонсирует новый сегмент всем клиентам. Все клиенты срочно начинают его скачивать и тут они получают кадр за кадром через http progressive download.

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

Это пока инициатива и в разработке, но может стать интересным.

Итого: от кадрового буфера на сервере можно в принципе и отказаться, если пользуемся не HLS, но даже если HLS, то при особых условиях можно что-то и придумать.

Сетевой буфер на отправку


Мы подошли к самой мякотке, камню преткновения и бесконечных метаний видеодоставки: UDP или TCP? Потери или непредсказуемые задержки? Или может совместить?

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

Как только начинаем слать видео по TCP, возникает вопрос не только о выборе протокола, дающего возможность порезать поток на кадры, а ещё и размерах выходных буферов в ядре. Чем больше ядерный буфер, тем проще софту рассылать и тем меньше можно сделать переключений контекста. Опять за счёт роста задержки.

Увеличиваем ядерные буферы и быстро теряем контроль за скоростью скачивания — становится тяжело контролировать отправку кадров и на сервере становится непонятно: клиент скачивает видео или уже нет.

Если шлем по UDP, то надо решать, чего делать с потерей пакетов. Есть вариант с повторной пересылкой UDP пакетов (эдакий недо-TCP), но он требует буферизации на клиенте (см ниже). Есть вариант с организацией чего-то типа RAID-5 поверх сети: в каждый udp пакет кладется избыточность, позволяющая восстановить один пакет из, скажем, 5 (см FEC, Fountain Codes и т.п.). Это может требовать роста задержки на сервере для вычисления такой избыточности, а так же поднимает битрейт на 10-30%. Считается, что избыточность не требует экстра буфера на клиенте, или по крайней мере он будет 1-2 кадра, но не 5 секунд (125 кадров)

Есть более изощренный вариант: кодировать видео в H264 SVC, т.е. в один пакет положить данные для восстановления самого плохого качества кадра, в следующий данные для улучшения качества и так далее. Потом эти пакеты маркируются разным уровнем ценности и умный хороший добрый роутер по пути непременно догадается и начнет выкидывать самые ненужные кадры, плавно снижая качество.

Вернемся в реальный мир.

С FEC есть как хорошие обещания так и реалии от гугла: «XOR FEC не работает». Пока непонятно и непонятно уже очень давно. С другой стороны в спутникой доставке FEC давно используется, но там нет никакого другого контроля за ошибками.

С SVC всё хорошо, кроме того, что он никак не взлетит. Напоминает JPEG2000 или вейвлеты: всем хороши, но что-то вот не хватает для покорения мира. По факту используется в закрытых реализациях видеоконференций, где под контролем сервер и клиент, но сходу этим механизмом воспользоваться не получается.

R-UDP по факту сложен, замещает собой TCP, используется редко и хорошо применим там, где подойдет и HLS с его 30 секундами задержки. Есть опасность ввязаться в перереализацию TCP, что можно считать практически нерешимой задачей.

Есть мнение, что подобный подход с UDP хорошо годится на пересылке через каналы с гигантским RTT и потерями, потому что не тормозит пересылку на её подтверждение. Важный момент заключается в том, что в случае с видеостримингом тормозить отправляющего вообще не нужно: трафик подается ровно с той скоростью, с которой он нужен. Если начать тормозить, то можно вообще не передавать, а выбирать поменьше качество. В свою очередь TCP это очень общий протокол доставки и у него есть допущения, которые неверны для прямого эфира:

  • данные надо или передать все, или порвать соединение. Для прямой трансляции это не так, смело можно выбрасывать то, что не получилось послать, пусть лучше видео рассыпется квадратиками, чем начнет залипать
  • передачу данных можно притормозить, что бы потом ускоренно передать. И это тоже не актуально для прямого эфира: или суммарной толщины канала хватает для передачи, или нет. Быстрее или медленнее поток видео литься не будет (без перенастройки транскодера)

Следствием этого является тот факт, что большой пинг на длинном расстоянии может начать тормозить TCP, хотя пакеты ходят быстро. UDP будет пересылать пакеты со скоростью реального времени: не быстрее, но и не медленее и никакого подтверждения доставки не требуется.

Доставка до клиента


Рост задержки при доставке от сервера к клиенту состоит из самой задержки передачи пакетов и процента потерь. Высокие потери будут приводить к торможению доставки из-за перепосылки данных в случае с TCP. В случае с UDP чаще будут включаться механизмы восстановления, или чаще будет рассыпаться видео.

В любом случае здесь немного помогают принудительные выборы маршрута типа: не слать видео напрямую из Москвы в Тайланд, а сделать это через облако Амазона в Сингапуре (личный опыт), но чудес нет, в скорость света мы давно уперлись, так что способов кроме физического перемещения поближе и не подсказать.

Эта часть может как уложиться в 10 мс, так и растянуться на 300 мс (на таком RTT вообще сложно добиться приличной скорости).

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

Самое забавное, что главная проблема может возникнуть на последнем метре от вайфай роутера до ноутбука. Иногда достаточно воткнуть кабель в ноутбук, что бы удивиться тому, каким может быть быстрым интернет.

Продолжение следует. В следующей публикации рассмотрим что происходит у клиента.

Комментарии (33)


  1. Zairo
    14.08.2017 20:42
    +2

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


    1. questor
      14.08.2017 21:53
      +3

      Кликбейт такой кликбейт.


  1. questor
    14.08.2017 21:52

    Насчёт предложения переписать tcp и о том, что это долго. Так подумать, а в сфере протоколов низкого уровня ничего и не менялось десятилетие, большой срок для ИТ. Это потому что найден золотой ключ, ничего не нужно улучшать? Или все же (как для стриминга) потребность есть но дорого, долго и никто из крупных корпораций не подписался на это?


    1. erlyvideo Автор
      14.08.2017 22:17

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

      во-вторых, стриминг видео действительно немного отличается от данных. Тут очень ровные потоки данных: всплесков трафика зачастую не бывает. Во-вторых можно терять данные, выбрасывать их. Из потока текстовых данных обычно выбрасывать ничего не хочется.


      1. ValdikSS
        14.08.2017 22:22
        +1

        Вы используете TCP BBR congestion control? Google использует его для раздачи видео YouTube, сильно помогает.


        1. erlyvideo Автор
          14.08.2017 22:23

          да, есть несколько разных рекомендаций. С bbr не сталкивался, есть рекомендации по hybla.

          честно говоря, лично я ни разу не видел, что бы у клиента всё было плохо и смена tcp cc всё делала сразу хорошо. Как правило смена OVH на хостинг помогает сразу и радикально.


          1. AlexWinner
            15.08.2017 11:53

            А что не так с OVH? Стоят железные сервера, всё ништяк. Кроме тех случаев, когда у них с сетью что-то, но это редко бывает.


        1. Aingis
          15.08.2017 12:22

          Стриминг на ютюбе далеко не гладкий. Периодические зависания для них вполне норма.


      1. JC_IIB
        15.08.2017 06:24

        Чисто теоретический вопрос — SCTP нельзя использовать в качестве транспорта? Он как раз про надежность и про ровные потоки данных, в тяжелых телекомах он весьма популярен. Хотя я не уверен, что он проедет через public internet, где-то едет, где-то — нет.


        1. erlyvideo Автор
          15.08.2017 09:13

          можно и где-то могут даже пользоваться. Можно даже указать на то, что SCTP сразу multihomed и позволяет слать пакеты по разным каналам (что бы было веселее собирать на клиентской части), но пока это не массово, как и сам SCTP. Он есть в webrtc для передачи данных и им пользуются для torrent-like вещания, ошибочно называя это p2p стримингом, но это всё таки про HLS.


      1. caban
        15.08.2017 06:59

        А на всякие варианты по-верх UDP не смотрели (enet)?


        1. erlyvideo Автор
          15.08.2017 09:16

          классическая схема с доставкой по UDP выглядит так: рассылающий сервер хранит у себя последние N пакетов. Принимающий клиент держит буфер и если в буфере оказываются дырки (все пакеты нумерованные), то он шлет серверу команду: перепошли мне пакет.

          Основная рассылка при этом вообще может делаться мультикастом и тогда можно обслуживать огромное количество абонентов без потерь и с низкими затратами.

          Но что-то такая схема не особо пошла.


    1. caban
      15.08.2017 07:04

      Как было сказано на одной из конференций: «Представьте что вам нужно транспортное средство, которое одновременно будет ездить по асфальту, луне, пустыне и болоту, оно будет ездить, но везде плохо, например по скорости проиграет обычному автомобилю на асфальте» Так и TCP он должен работать в ДЦ c 40G каналами и через спутник, где высокое Latency, поэтому он такой какой есть, его улучшают, но придумать что-то такое же универсальное, очень тяжело.


      1. erlyvideo Автор
        15.08.2017 09:17

        да вот как-то получилось, что сравнение то так себе: TCP в целом везде неплох и его очень сложно поменять на что-то другое.


  1. ogregor
    14.08.2017 22:14

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


    1. erlyvideo Автор
      14.08.2017 22:16

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

      Надо ставить метки абсолютного времени на кадры (UTC), но это очень сложно сделать, потому что IP камера не может сообщить, когда был снят кадр, можно лишь узнать, когда он был получен в сжатом виде, т.е. надо мерять ту самую задержку в энкодере.


  1. byria
    15.08.2017 02:31

    В мире беспилотников ( любительских) в FPV сегменте, до сих пор нет хорошей связки цифрового видео тракта и передачи видео картинки в очки (с минимальной задержкой < 50 мс). Обычно это либо HD картинка максимум, но большая задержка, либо намного хуже качество и аналоговая, но с минимум задержки, либо рвется связь видео потока. Исключение DJI продукция. Хотелось бы узнать, как с высоты вашего опыта можно было бы реализовать ( и на каких решениях, протоколах, кодеках) передачу FullHD ( или выше) с носителя, где видео тракт, скажем, максимум по весу = 500 грамм с камерой на борту, мог бы передаваться по усиленному каналу WIFI ( 500 метров и больше), для трансляции в FPV шлем full hd цифрой. Очень насущная проблема. И да, очень важна минимальная задержка ( идеал <25 мс).


    1. erlyvideo Автор
      15.08.2017 11:20

      я бы думал в таком направлении: писать оригинальное качество на флешку, а слать возможно плавающее качество. Транспортом выбрать UDP, возможно FEC. И поглубже поковыряться в wifi: там есть свои задержки, лаги и ретрансмиты.


  1. Durimar123
    15.08.2017 10:59

    А у вас нет таблицы типа протокол/size/Cpu/latency?

    Из собственных экспериментов могу сказать, что самый быстрый протокол из тех, что я сумел настроить и использовать это WebRTC.


    1. erlyvideo Автор
      15.08.2017 11:12

      webrtc стандартный для новых браузеров и по udp, поэтому у него не будет (не должно быть) плавающей задержки.