Низкая задержка: LL-HLS, LL-DASH и бюджет

Автор: Николай СапуновОбновлено: август 202618 мин чтения
Содержание статьи +

TL;DR

Обычный HTTP-стриминг медленный по замыслу: типичный плеер HLS или DASH держит на руках около трёх сегментов видео до старта, поэтому при сегментах по шесть секунд зритель уже отстаёт от реальности на восемнадцать–тридцать секунд – нормально для фильма, фатально для матча, где соседи кричат «гол» раньше, чем это видит ваш экран. Оба стандарта низкой задержки, Low-Latency HLS (LL-HLS) и Low-Latency DASH (LL-DASH), решают это одинаково: режут каждый сегмент на куда более мелкие CMAF-чанки и отдают каждый чанк сразу, как только он закодирован, опуская задержку glass-to-glass с двадцати–тридцати секунд до примерно двух–пяти, не выбрасывая дешёвую, дружелюбную к кэшу доставку через CDN, которая и сделала HTTP-стриминг победителем. Подвох в реальном компромиссе, который спецификации признают вслух: гнаться за меньшей задержкой – значит брать более мелкие куски, делать больше запросов, ронять cache-hit и истончать страховочный буфер, поэтому та же правка, что выигрывает две секунды, повышает риск ребуферинга и нагрузку на origin. Эта статья – про бюджет задержки: где на самом деле прячутся секунды, какой рычаг двигает какую секунду, во что обходится каждая секунда в стабильности и счетах CDN, и про продуктовый вопрос, который идёт перед всем остальным: насколько низко вам реально нужно?

Почему это важно

Задержка – одно из немногих стриминговых чисел, которое нетехнический зритель замечает сразу и судит строго: для прямого спорта, новостей, аукционов, ставок и любого «смотрим вместе» отставание на тридцать секунд – это не нюанс качества, а сломанный продукт, потому что пуш-уведомление, сообщение от друга или крик через стену рушат момент раньше, чем экран его догонит. Но низкая задержка не бесплатна, и самая дорогая ошибка команд – относиться к «сделать риалтайм» как к тумблеру, а не как к бюджету со статьями и счётом. Эта статья – для основателя, продакт-менеджера или стриминг-инженера, которому нужно решить, насколько низко идти, поставить кодеру и CDN-вендору правильную цель и понять, почему погоня за последней секундой тихо растит ребуферинг и стоимость CDN для всех. К концу вы сможете читать бюджет задержки построчно, объяснять, почему обычный HLS медленный и как LL-HLS и LL-DASH его ускоряют, и ставить разумное число на задержку, которая вашему продукту реально нужна, – ни беспечно высокое, ни дорого низкое.

Минута на освежение: откуда берутся секунды

Эта статья опирается на как CDN доставляет видео и на механику упаковки из упаковка: CMAF, HLS и DASH из одного mezzanine; вот одна идея, которую нужно держать перед глазами.

У задержки, которую чувствует зритель, есть имя. Glass-to-glass-задержка – это разрыв в секундах между моментом, когда линза камеры что-то поймала (первое «стекло»), и моментом, когда это появляется на экране зрителя (второе «стекло»). Это не одна задержка, а цепочка маленьких, сложенных вместе: камера и буфер захвата, кодер, что сжимает картинку, упаковщик, что заворачивает её в файлы доставки, контрибуционный канал до вашей платформы, сеть доставки контента (всемирный флот кэширующих серверов, или CDN, что держит копии вашего видео рядом со зрителями), буфер плеера (те несколько секунд видео, что плеер держит в запасе, чтобы сетевой сбой не заморозил картинку) и обновление самого экрана.

Вот факт, который организует всё ниже: в этой цепочке почти каждое многосекундное число со спецификации приходит из одного блока – буфера плеера. Сложите кодер, упаковщик, контрибуцию, CDN и декод – и редко выйдете за секунду неизбежной работы. Буфер плеера – тот член, что ведёт итог от четверти секунды до двадцати пяти, потому что буфер – это намеренная подушка, а её размер по большей части выбор. Низкая задержка – это почти целиком инженерия меньшей подушки, что всё ещё гасит толчки.

Рис. 1. Бюджет задержки. Захват, кодирование, упаковка и CDN складываются примерно в секунду во всех режимах; буфер плеера – решающий член, что ведёт итог от ~25 секунд (plain HLS) до ~2–5 секунд (низкая задержка) и субсекунды (WebRTC).

Почему обычные HLS и DASH медленны по замыслу

Чтобы видео выжило в открытом интернете, HTTP-стриминг – доставка видео обычными веб-файлами по тому же протоколу, что отдаёт веб-страницы, – рубит поток на сегменты, короткие файлы по несколько секунд, и пишет манифест (плейлист, перечисляющий сегменты по порядку). Плеер скачивает манифест, затем тянет сегменты один за другим. Это и есть тот замысел, что позволил стримингу ехать на дешёвых обычных CDN и достигать миллионов зрителей – разобрано в как CDN доставляет видео.

Медлительность вшита в правило безопасности. Чтобы не замёрзнуть при дрожании сети, плеер держит запас уже скачанного видео до старта – и стандарты вшивают, сколько именно запаса. HTTP Live Streaming (HLS), определённый Apple в IETF RFC 8216 и его втором издании, через значение HOLD-BACK велит плееру стартовать не ближе трёх Target Duration от конца live-плейлиста; спецификация говорит, что это значение «MUST be at least three times the Target Duration», и при отсутствии значения плеер берёт ровно столько. Развёртывания MPEG-DASH используют то же правило – эталонная конфигурация DASH-IF ставит буфер плеера в «3 times maximum/target segment duration». Три сегмента подушки – это то, что держит воспроизведение гладким; это же и делает обычный HTTP-стриминг далёким от live.

Пройдём арифметику вслух – размер числа и есть вся проблема:

Правило live-края: старт ≈ 3 × длительность сегмента от live
   (HLS HOLD-BACK ≥ 3 × Target Duration; буфер DASH ≈ 3 × сегмент)

Сегменты по 6 с:   3 × 6 с  = 18 с от live (до прочих задержек)
Сегменты по 10 с:  3 × 10 с = 30 с от live

Плюс кодер + упаковщик + контрибуция + CDN + декод (~1–2 с)
   → реальный plain-HLS/DASH live стоит ~20–30 с от реальности.

Вот почему ранние рекомендации самой Apple советовали десятисекундные сегменты, и почему, как прямо пишет отчёт DASH-IF, легаси-настройки – «10 seconds segments, buffer 2–3 segments at the client» – «immediately result beyond 30 seconds end-to-end latency». Обычный HTTP-стриминг, по словам Apple, никогда не задумывался как система низкой задержки. Очевидная починка – просто сделать сегменты крошечными – не работает: короткие сегменты значат больше файлов, больше запросов, худшую компрессию и выше шанс ребуферинга. Стандартам низкой задержки нужен был способ доставлять куски меньше сегмента без налога мелких сегментов. Этот способ – CMAF-чанк.

Рис. 2. Правило трёх сегментов. Обычный плеер держит около трёх сегментов до старта, поэтому шестисекундные сегменты ставят зрителя на ~18 секунд позади live, а десятисекундные – на ~30, ещё до учёта сетевой задержки.

Один общий трюк обоих стандартов: CMAF-чанк

Вот идея, на которой работают оба – и LL-HLS, и LL-DASH, – и это самое важное в статье. Сегмент не обязан доставляться одним готовым файлом. Common Media Application Format (CMAF), стандартизированный как ISO/IEC 23000-19, определяет меньшую единицу – CMAF-чанк: один или несколько кадров видео в самодостаточной коробочке (технически пара moof-заголовок плюс данные mdat), которую можно прочитать и декодировать саму по себе, ещё до того, как существует остаток родительского сегмента.

Это полностью меняет тайминг. Вместо того чтобы кодер держал целый шестисекундный сегмент до последнего кадра и затем отдавал один большой файл, упаковщик выдаёт поток крошечных чанков – часто около 200 миллисекунд каждый – сразу, как только каждый закодирован. Транспорт, что их несёт, – HTTP chunked transfer encoding, штатная возможность HTTP, что позволяет серверу начать отправку тела ответа до того, как он знает полную длину, и дописывать его. Так сервер начинает отправлять первый чанк сегмента, пока ещё производит последний, а плеер у live-края потребляет чанки по мере прихода, не дожидаясь закрытого файла. Ожидание целого сегмента – главная задержка обычного HTTP-стриминга – исчезает, а сами сегменты остаются комфортной длины в несколько секунд, поэтому эффективность компрессии и поведение кэша почти не меняются.

Из этого следует две вещи, и они важны для остатка статьи. Первая: CMAF-чанк – это общий строительный блок: LL-HLS и LL-DASH – это два манифеста и два набора сигнализации вокруг одной чанкованной медиа. Вы кодируете и чанкуете один раз и пакуете результат под оба. Это младший родственник идеи «зашифровать раз, упаковать под оба» из упаковки: CMAF, HLS и DASH. Вторая: чанк – ровно то место, где проявляется стоимость: больше мелких кусков значат больше запросов и ниже cache-hit, к чему мы вернёмся, когда будем считать счёт.

Рис. 3. Общий строительный блок. Кодер выдаёт CMAF-чанки по ~200 мс по мере производства; HTTP chunked transfer отправляет каждый чанк до завершения сегмента; те же чанки питают и манифест LL-HLS, и манифест LL-DASH.

LL-HLS: parts, preload hints и плейлист, который ждёт

Low-Latency HLS, добавленный в HLS в его втором издании (продолжающейся ревизии RFC 8216), показывает эти чанки плееру как Partial Segments, обычно просто parts (части). Словами самой спецификации, partial segments «provide a parallel channel for distributing media at the live edge», где медиа «is divided into a larger number of smaller pieces, such as CMAF Chunks», так что каждая часть «can be packaged, published, and added to the Media Playlist much earlier than its Parent Segment». Часть помечается тегом EXT-X-PART; плейлист объявляет длину части значением PART-TARGET. Зрителю разрешено играть куда ближе к live: где правило целого сегмента держало плеер на три сегмента позади, правило низкой задержки PART-HOLD-BACK должно быть «at least twice the Part Target Duration» и «SHOULD be at least three times» – так что при частях по 200 мс live-край теперь хорошо меньше секунды удержанной медиа, а не восемнадцать.

Три меньших механизма делают это быстрым, не плавя сервер, и вам не нужно их реализовывать, чтобы поставить вендору задачу, – нужно знать, что они есть. Preload hints (EXT-X-PRELOAD-HINT) дают плейлисту сообщить плееру URL следующей части до того, как она существует, так что запрос плеера уже в полёте, когда часть приходит. Blocking Playlist Reload позволяет плееру попросить плейлист, которого он ещё не видел – он добавляет параметры _HLS_msn (media sequence number) и _HLS_part к запросу, – а сервер держит ответ открытым, пока этот более новый плейлист не готов, вместо того чтобы заставлять плеер опрашивать снова и снова. Сервер объявляет это через CAN-BLOCK-RELOAD=YES. А Delta Updates и Rendition Reports держат постоянные обновления плейлиста маленькими и позволяют плееру, что переключает качество, сохранить позицию низкой задержки. О покадровой механике каждого тега разбор LL-HLS в разделе Video Streaming идёт глубже, чем мы здесь; на уровне продукта суть проста: parts несут медиа, preload hints и blocking reload убирают round trips, и зритель оказывается на две–пять секунд позади live.

LL-DASH: чанкованные сегменты и манифест, что объявляет цель

Low-Latency DASH приходит к тому же со стороны DASH. MPEG-DASH (ISO/IEC 23009-1) добавил режим низкой задержки, что сигналит плееру: сегмент «can be accessed earlier than full availability at the server» – то есть его можно тянуть и играть как чанкованные, прогрессивные данные, а не как закрытый файл. Медиа – та же чанкованная CMAF; разница в манифесте, DASH-овском MPD (Media Presentation Description). MPD несёт пару сигналов низкой задержки, что читает плеер: availabilityTimeOffset, что говорит плееру – сегмент доступен раньше номинального времени (так он может начать тянуть посреди сегмента), и элемент ServiceDescription, что заявляет целевую задержку оператора – live-позицию, к которой плеер должен стремиться и держать. Доставка снова едет на HTTP chunked transfer от упаковщика до edge CDN и далее до плеера.

Практический итог: LL-DASH и LL-HLS куда более похожи, чем намекают имена протоколов. Оба едут на CMAF-чанках ~200 мс; оба используют HTTP chunked transfer; оба держат обычные сегменты длиной в несколько секунд, чтобы кэш и компрессия оставались здоровыми; оба попадают в диапазон две–пять секунд. Различаются они манифестом и мелким шрифтом того, как плеер гонится за live-краем: LL-HLS опирается на blocking playlist reload и preload hints, LL-DASH – на availability-offset в MPD и целевую задержку, к которой плеер серво-управляет. Честная инженерная реальность 2026-го в том, что обычно вы отдаёте оба, потому что поддержка устройств расколота: экосистема Apple (Safari, iOS, tvOS) – это мир LL-HLS, тогда как многие смарт-ТВ, Android и веб-плееры крутят DASH – тот же раскол покрытия клиентов, что разобран в клиентской матрице OTT. Протокольное сравнение живёт в статье о низкой задержке раздела Video Streaming; платформенное решение – кодируем и чанкуем раз, пакуем под оба – то, чем владеет этот раздел.

Рис. 4. Два манифеста, одна медиа. LL-HLS использует parts, preload hints и blocking playlist reload; LL-DASH – чанкованные сегменты, availability-offset и заявленную целевую задержку. Оба едут на одних CMAF-чанках и оба попадают в ~2–5 секунд.

Считаем бюджет задержки: куда уходит каждая секунда

Числа делают это конкретным. Ниже – иллюстративный бюджет glass-to-glass для одного и того же live-фида, доставленного тремя способами: plain HLS/DASH, низкая задержка (LL-HLS/LL-DASH) и WebRTC, риалтайм-протокол для субсекундного взаимодействия (ссылка ниже). Строки захвата, кодирования, упаковки, контрибуции, CDN и декода почти не двигаются между тремя; буфер плеера – та строка, что решает продукт.

Статья задержкиPlain HLS/DASHНизкая задержка (LL-HLS/LL-DASH)WebRTC
Захват + буфер камеры~0,1 с~0,1 с~0,1 с
Кодер~0,2 с~0,2 с~0,15 с
Упаковщик (сегмент vs чанк)~6 с (ждёт целый сегмент)~0,2 с (выдаёт каждый чанк)н/д
Контрибуция + CDN~0,3 с~0,3 с~0,2 с
Буфер плеера (решающий член)~12–22 с (≈ 3 сегмента)~1–3 с (несколько чанков)~0,1 с
Декод + рендер~0,1 с~0,1 с~0,1 с
Итог glass-to-glass~20–30 с~2–5 с~0,3–0,5 с

Таблица 1. Иллюстративный бюджет задержки для одного фида, доставленного тремя способами. Всё, кроме буфера плеера (и, для plain HTTP, ожидания целого сегмента упаковщиком), примерно постоянно; буфер – там, где секунды выигрываются или теряются. WebRTC выигрывает по задержке, но не едет на обычных CDN так, как HLS/DASH, поэтому это выбор для взаимодействия, а не для массового one-to-many масштаба – см. ссылку ниже. Числа иллюстративны; мерьте свой пайплайн.

Читайте таблицу как набор рычагов. Строка упаковщика схлопывается с шести секунд до пятой доли секунды в момент перехода от доставки целым сегментом к чанкованной CMAF – это выигрыш CMAF-чанка. Строка буфера плеера падает с трёх сегментов до нескольких чанков в момент, когда плееру разрешено сидеть у live-края (PART-HOLD-BACK в LL-HLS, целевая задержка в LL-DASH) – это выигрыш режима низкой задержки. Прочие строки – физика и обработка, которые не отменить желанием; заметьте, что строки контрибуции и CDN несут налог расстояния из географии, регионов и глобальной доставки, так что зритель на другом континенте начинает игру в низкую задержку с худшей рукой.

Во что обходится низкая задержка: компромисс, что признают спецификации

Теперь часть, которую вендорские демо пропускают. Меньшая задержка покупается, и валюта – стабильность. Спецификация HLS заявляет компромисс прямым текстом: «A shorter Target Duration reduces latency but also reduces available buffer, handicaps adaption and increases delivery overhead, increasing the likelihood of playback stall. A longer Hold Back can mitigate the playback stall likelihood, but increases the latency». Отчёт DASH-IF говорит то же с другой стороны: «the further behind live the player chooses to play, the more stable the delivery system is, which leads to antagonistic demands on any production system of low latency and stability». Низкая задержка и надёжность тянут в разные стороны; вы выбираете точку на прямой, а не щёлкаете тумблером.

Стоимость проявляется в трёх местах, и владельцу платформы стоит оценить все три. Первое: страховочная подушка тоньше: у плеера, что сидит на секунду позади live, есть секунда, чтобы пережить провал полосы до ребуферинга, а у плеера на двадцать секунд позади – двадцать. Опустите задержку слишком низко для реальной сети аудитории – и смените медленный старт на спиннер посреди потока, обычно худший опыт. Второе: растёт частота запросов и падает cache-hit: вместо одного запроса на шестисекундный сегмент плеер теперь делает много мелких запросов в секунду за parts или чанки, а крошечные, часто меняющиеся объекты кэшу труднее держать, чем большие стабильные. Ниже эффективность кэша – больше трафика доходит до origin, что прямо связано с работой над edge-кэшированием и ключами кэша и origin и origin shielding, что нужны, чтобы origin выжил. Третье: счёт CDN и origin растёт: больше запросов и ниже offload значат больше egress origin и больше edge-транзакций – статьи из cost engineering для CDN, – а live-премьера заставляет всё это всплеснуть разом, тема доставки живых событий и всплеска премьеры.

Так что вопрос не «насколько низко мы можем», а «насколько низко нам нужно», и честный ответ зависит от продукта, а не от технологии. У фильма или бэк-каталога SVOD вообще нет live-ориентира – задержка неважна, тратьте бюджет на качество и стабильность. Канал новостей или ток-шоу комфортен на пяти–десяти секундах. Прямой спорт, аукционы, live-шопинг и ставки реально требуют диапазона низкой задержки две–пять секунд, потому что запоздалая реакция ломает продукт. А по-настоящему интерактивные, субсекундные сценарии – видеозвонок, аукционист, принимающий ставки вживую, watch-party, где люди отвечают, – обычно вовсе не задача LL-HLS/LL-DASH, а WebRTC, другой архитектуры, что меняет CDN-масштаб на риалтайм-взаимодействие; где проходит эта грань, мы разбираем в статье о низкой задержке раздела Video Streaming. Выбор правильного уровня – это решение о масштабе не меньше, чем о задержке, поэтому оно стоит рядом с масштабированием и конкурентностью.

Рис. 5. Насколько низко нужно? Потребность в задержке растёт от видео по запросу (цели нет) через новости (5–10 с) к прямому спорту, аукционам и ставкам (2–5 с через LL-HLS/LL-DASH); настоящий интерактив (субсекунда) – задача WebRTC. Ниже задержка стоит стабильности и бюджета CDN – подбирайте уровень под продукт.

Частые ошибки, из-за которых низкая задержка бьёт по своим

Большинство провалов низкой задержки – не баги стандартов; это цель, выбранная без бюджета.

Главная ошибка – гнаться за числом задержки, которое сеть аудитории не держит: ставить односекундный live-край для мобильной аудитории на переменных каналах, так что тонкий буфер пустеет на первом провале, и зритель меняет терпимый медленный старт на нетерпимое замерзание посреди потока. Её родственник – дробить сегменты вместо чанкования: уходить в сегменты по одной–две секунды, чтобы срезать задержку грубо, что множит запросы, рушит компрессию и роняет cache-hit без чистого выигрыша, что дают CMAF-чанки. Третья – забыть про последствия для origin и кэша: включить низкую задержку, не пересмотрев ключи кэша, origin shielding и surge-ёмкость, так что лишняя нагрузка запросов тихо плавит origin на первом популярном live-событии. Четвёртая – мерить задержку в офисе: подтвердить две секунды на студийном LAN и отгрузить, когда реальный зритель за два континента, неся налог расстояния из географии и глобальной доставки, этого числа не видит. Пятая – платить за низкую задержку там, где продукту она не нужна: строить субпятисекундный пайплайн для каталога фильмов, тратя стабильность и бюджет CDN на секунды, которых зритель не заметит. И тихая шестая – отгрузить только один протокол: построить LL-HLS и пропустить LL-DASH (или наоборот), молча оставив половину матрицы устройств на медленном фолбэке или вовсе без потока – разрыв покрытия, что клиентская матрица OTT и существует, чтобы закрыть.

Где здесь Фора Софт

Низкая задержка – это решение о масштабе и стоимости раньше, чем о протоколе: реальный вопрос не «можем ли мы сделать риалтайм», а «сколько секунд этому продукту реально нужно, во что обходится каждая секунда ниже линии комфорта в ребуферинге и счетах CDN, и какие устройства мы обязаны достать». Фора Софт строит видеостриминг, OTT/Internet TV, live-события, WebRTC и интерактивное видео с 2005 года, на 250+ отгруженных проектах для 400+ клиентов, и эта работа идёт прямо через этот слой: задаём цель по задержке от продукта, а не от хайпа, чанкуем раз и пакуем под оба – LL-HLS и LL-DASH, – чтобы покрыть всю матрицу устройств, настраиваем hold-back плеера у live-края под реальные сети аудитории и – поскольку низкая задержка растит нагрузку запросов – соединяем это с работой по кэшу, origin-shield и surge-ёмкости, что держит платформу стабильной, когда премьера всплёскивает. Когда медиакомпании нужен live, что реально на несколько секунд позади реальности на каждом экране и всё ещё доступен на масштабе, эта инженерия «сначала бюджет» – то, что мы приносим.

Ключевые выводы

  • Plain HLS/DASH стоит ~20–30 с от live, потому что плееры держат ~3 сегмента (HLS HOLD-BACK ≥ 3× Target Duration).
  • Буфер плеера – решающий член: он ведёт итог от субсекунды до 25 с; всё остальное – около 1 с.
  • LL-HLS и LL-DASH оба режут сегменты на CMAF-чанки ~200 мс, отдаваемые по HTTP chunked transfer по мере кодирования.
  • Одна чанкованная медиа, два манифеста: отдавайте и LL-HLS, и LL-DASH, потому что поддержка устройств расколота.
  • Спецификации признают компромисс: ниже задержка – тоньше буфер, больше запросов, ниже cache-hit, выше риск ребуферинга и счёт.
  • Спрашивайте «насколько низко нам нужно?» – VOD: никак; новости: 5–10 с; спорт/ставки: 2–5 с; интерактив: субсекундный WebRTC.

Что почитать дальше

Строите такую систему?

Подберём параметры кодирования под ваш контент и посчитаем стоимость доставки до старта разработки.