LL-HLS подробный разбор: parts, preload-хинты, blocking reload, rendition reports

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

Опубликовано: 2026-05-21 · Время чтения: 24 мин · Автор: Николай Сапунов, CEO Фора Софт

Последняя сверка: 2026-05-21 по IETF RFC 8216 (HTTP Live Streaming, август 2017), draft-pantos-hls-rfc8216bis-22 (HTTP Live Streaming 2nd Edition, 1 мая 2026), Apple HLS Authoring Specification for Apple Devices ревизия 2025-09, доклад Apple WWDC 2025 «What's new in HTTP Live Streaming», ISO/IEC 23000-19:2024 (CMAF), Bitmovin Video Developer Report 2025/26.

TL;DR

Low-Latency HTTP Live Streaming – LL-HLS – это расширение обычного HLS, которое добавляет пять механизмов, чтобы зритель видел прямую трансляцию через 2–5 секунд после события, а не через 20–30 секунд, как у классического HLS. Пять механизмов: частичные сегменты (тег #EXT-X-PART), подсказки предзагрузки (#EXT-X-PRELOAD-HINT), блокирующая перезагрузка плейлиста (флаг #EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES и query-параметры плеера _HLS_msn / _HLS_part), отчёты по рендициям (#EXT-X-RENDITION-REPORT) и дельта-обновления плейлиста (CAN-SKIP-UNTIL плюс тег #EXT-X-SKIP). Вместе они заменяют цикл «запросил – подождал – повторил» обычного HLS на long-poll: сервер удерживает каждый запрос плейлиста открытым, пока не появится следующий кусочек медиа, и сразу же его отдаёт, а плеер успевает запустить следующий long-poll до того, как обработает ответ на предыдущий. Требование HTTP/2 server push, с которого начинался LL-HLS в 2019 году, Apple убрала из HLS Authoring Specification в ревизии сентября 2023; в 2026 LL-HLS работает поверх обычного HTTP/1.1 с chunked transfer, HTTP/2-стримов или HTTP/3-стримов с chunked-CMAF-сегментами, которые умеет генерировать любой современный пакетизатор.

Зачем это нужно

Обычный HLS – великолепный, дружелюбный к CDN формат доставки и одновременно ужасный интерактивный медиа-канал. Сегмент длиной 6 секунд плюс буфер воспроизведения из трёх сегментов – это уже 18 секунд задержки до того, как заговорят кодировщик, сеть и буфер плеера; в реальных продакшен-сборках glass-to-glass-задержка устойчиво держится в районе 25–30 секунд. Для фильма это нормально, а для ставок на спорт, прямых аукционов, обучения с Q&A, видеонаблюдения, телемедицины и live-shopping – катастрофа: реакция зрителя должна попадать в тот же разговор, что и событие, а не в его повтор. LL-HLS – это протокол, который вы выбираете, когда хотите экономики HLS и совместимости с экосистемой Apple, но при этом нужен зритель, который твитнет про гол во время матча, а не после повтора. Механика тонкая, формы отказа неочевидные, а неправильная настройка даёт буферизацию вместо низкой задержки. Цель этой статьи – сделать каждую строку LL-HLS-плейлиста читаемой и заранее показать компромиссы, прежде чем они выльются в счёт от CDN за сезон.

Что такое LL-HLS – одним абзацем

LL-HLS – это обычный HLS плюс пять тегов и одно HTTP-поведение. Производитель кодирует прямой эфир так же, как для обычного HLS, но вместо того чтобы ждать шесть секунд до публикации полного сегмента, пакетизатор режет каждый сегмент на 10–30 частичных сегментов (или parts, или чанков в терминах CMAF) по 200–500 мс и публикует каждый part сразу, как только тот готов. Медиа-плейлист теперь содержит не только закрытые сегменты сверху, но и parts текущего, ещё не закрытого сегмента – снизу. Новый тег #EXT-X-PART отмечает каждый part его длительностью и URI; другой, #EXT-X-PRELOAD-HINT, заранее сообщает плееру URI следующего part'а ещё до того, как тот появится. Плеер использует long-poll-запрос – playlist.m3u8?_HLS_msn=42&_HLS_part=7 – со смыслом «верни мне плейлист, когда в нём появится part 7 сегмента 42»; сервер удерживает этот запрос открытым, пока part не будет готов, и тут же возвращает ответ. Плеер успевает поставить в очередь следующий long-poll до того, как разберёт текущий ответ. На выходе вместо ритма «остановка – запрос» получается непрерывный поток медиа, а glass-to-glass-задержка падает с 25 секунд до 2–5.

Протокол представили на WWDC 2019 как Apple Low-Latency HLS – расширение поверх существующей спецификации HLS. Первоначальный дизайн 2019 года полагался на HTTP/2 server push для доставки parts; индустрия его критиковала, потому что коммерческие CDN плохо его поддерживали в продакшене. В ревизии сентября 2023 Apple убрала требование HTTP/2 push и заменила его механизмом preload-хинтов, который описан ниже. Сейчас спецификация – часть того же Internet-Draft, что отслеживает остальной HLS, – draft-pantos-hls-rfc8216bis-22, опубликованный 1 мая 2026, – а для устройств Apple нормативные требования поверх неё прописаны в HLS Authoring Specification ревизии 2025-09.

Бюджет задержки: до и после

Самый понятный способ увидеть, что делает LL-HLS, – положить бюджет задержки рядом с обычным HLS и посмотреть, как сокращается каждый сегмент полосы.

У типичной прямой трансляции на обычном HLS задержку формируют три слагаемых. Кодировщик выдаёт сегменты по шесть секунд; до того как первый байт сегмента 42 окажется на origin-сервере, проходит шесть секунд реального времени. Обновление плейлиста – это один round-trip за каждый target-duration, то есть в среднем половина target-duration ожидания между появлением нового сегмента и моментом, когда плеер о нём узнаёт; для шестисекундного target – это 3 секунды. Буфер воспроизведения по конвенции занимает 3× target-duration, чтобы плеер мог глотать сетевой джиттер без ребуферинга, – ещё 18 секунд. Добавьте около секунды HTTP-оверхеда – и получится ~28 секунд glass-to-glass. Продакшен-замеры – 20–30 секунд.

LL-HLS переписывает каждую строку.

Слагаемое задержкиОбычный HLS (сегменты 6 с)LL-HLS (сегменты 6 с, parts 200 мс)
Кодировщик6.0 с (целый сегмент)0.2 с (один part)
Обновление плейлиста3.0 с (половина target-duration)0.0 с (blocking reload возвращает мгновенно)
Буфер воспроизведения18.0 с (3× target-duration)0.6–1.2 с (3–6 parts)
HTTP-оверхед1.0 с0.4 с (pipelined)
Итого glass-to-glass~28 с~2.2–3.0 с

Строка «Кодировщик» падает с 6 с до 200 мс, потому что пакетизатор публикует part сразу, как только тот готов, а не дожидается полного сегмента. Строка «Обновление плейлиста» обнуляется, потому что сервер удерживает long-poll открытым до появления part'а и возвращает ответ в тот же момент, как тот появляется, – round-trip на опрос не нужен. Буфер сокращается с 3× target-duration до 3–6 parts, потому что минимальная единица, которой оперирует плеер, теперь part, а не сегмент. HTTP-оверхед сокращается вдвое, потому что запросы конвейеризованы. Математика прямая: 0.2 + 0.0 + 0.6 + 0.4 = 1.2 с протокольной задержки плюс 1–2 с на пайплайн кодировщика и предзагрузку декодера – итого LL-HLS попадает в полосу 2–3 с, которую и показывают продакшен-замеры в экосистеме Apple.

Рис. 1. Бюджет glass-to-glass-задержки: обычный HLS (верхняя полоса) против LL-HLS (нижняя). Каждый сегмент подписан своим вкладом; LL-HLS сокращает каждую строку на порядок.

Пять механизмов – по одному

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

Механизм 1 – Частичные сегменты (`#EXT-X-PART`)

Частичный сегмент, или part, – это срез 200–500 мс ещё не закрытого сегмента, адресуемый как отдельный HTTP-ресурс. Пока кодировщик ещё производит сегмент 42, пакетизатор уже нарезает и публикует parts 1, 2, 3 – каждый отдельным файлом (или байт-диапазоном внутри файла сегмента). Медиа-плейлист перечисляет каждый завершённый part новым тегом:

#EXT-X-PART:DURATION=0.20000,URI="seg42-p1.m4s",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.20000,URI="seg42-p2.m4s"
#EXT-X-PART:DURATION=0.20000,URI="seg42-p3.m4s"

DURATION – длительность part в секундах десятичным числом. Спецификация требует, чтобы сумма длительностей всех parts сегмента точно равнялась #EXTINF сегмента, когда тот закроется. URI указывает на part – относительно плейлиста, как и URI сегмента. Опциональный атрибут INDEPENDENT=YES помечает part, который начинается с I-frame, – это единственный вид part'а, который плеер может использовать как точку ABR-переключения или как первый кадр после seek'а; спецификация требует минимум один независимый part на сегмент, а Apple Authoring Specification рекомендует не реже одного независимого part'а в секунду.

Второй новый тег объявляет политику part'ов в начале плейлиста:

#EXT-X-PART-INF:PART-TARGET=0.2

PART-TARGET – целевая длительность part'а в секундах. Плеер использует её, чтобы заранее подобрать размер буфера; значение должно быть не меньше самого длинного part'а в плейлисте. Apple Authoring Specification требует PART-TARGET ≤ 0.33 (333 мс) для ультранизкой задержки и PART-TARGET ≤ 1.0 для расслабленного режима. 200 мс – безопасный дефолт; 300 мс – безопасный потолок.

Выбор PART-TARGET – главная ручка задержки в LL-HLS. Меньше parts – ниже задержка, больше запросов; крупнее parts – выше задержка, меньше запросов. Арифметика прямая: шестисекундный сегмент с 200-мс parts – это 30 parts, поэтому в стриме из 5 рендиций получается 150 part-запросов на 6 секунд (25 в секунду) вместо 5 сегмент-запросов на 6 секунд (меньше одного в секунду). Дизайн cache-key'ев на origin и CDN должен это выдерживать.

Механизм 2 – Подсказки предзагрузки (`#EXT-X-PRELOAD-HINT`)

После того как плеер забрал все parts из текущего плейлиста, он всё ещё не знает, где будет жить следующий part, пока не обновит плейлист. Preload-хинт сообщает это заранее:

#EXT-X-PRELOAD-HINT:TYPE=PART,URI="seg42-p4.m4s"

TYPE=PART объявляет, что следующее, что плееру стоит запросить, – это part. Плеер сразу выпускает обычный HTTP GET по этому URI. Сервер удерживает запрос открытым – файл ещё не существует, – а как только кодировщик публикует part, сервер стримит байты обратно по уже открытому соединению через HTTP/1.1 chunked transfer encoding, HTTP/2-стрим или HTTP/3-стрим. С точки зрения плеера он запросил следующий part – и следующий part пришёл. Round-trip «прочитать плейлист, найти URI, запросить part, дождаться ответа» схлопывается в одно уже открытое соединение.

Preload-хинты заменили HTTP/2 server push, который был исходным дизайном 2019 года. HTTP/2 push оказался тяжёлым в CDN-масштабе, потому что push-ответы обходили кеш-логику любых коммерческих CDN; Apple убрала требование в ревизии HLS Authoring Specification от сентября 2023. Ревизия сентября 2025 оставляет preload-хинты единственным нормативным способом доставить следующий part плееру до того, как тот URI окажется в обновлённом плейлисте. Статьи, в которых LL-HLS до сих пор требует HTTP/2 push, написаны до этой смены и устарели.

TYPE=MAP – вторая форма, используется для предзагрузки следующего CMAF-init-сегмента, когда кодировщик меняет параметры кодирования между сегментами. На практике в установившемся стриме это срабатывает редко и важно в основном на границах ABR-переключения.

Механизм 3 – Блокирующая перезагрузка плейлиста

Плеер запрашивает медиа-плейлист не по фиксированному расписанию, а long-poll'ом с указанием part'а, до которого сервер должен подождать:

GET /720p/playlist.m3u8?_HLS_msn=42&_HLS_part=7 HTTP/1.1
Host: edge.example.com

Два зарезервированных query-параметра – _HLS_msn (media sequence number, которое плеер хочет увидеть в ответе) и _HLS_part (индекс part'а внутри этого сегмента) – это delivery-директивы, определённые в спецификации. Сервер читает их и ведёт себя так: если уже есть плейлист с part'ом 7 сегмента 42 – возвращает его сразу; если нет – удерживает запрос открытым, пока part не появится, и тогда возвращает плейлист; если ожидание превысит 3× target-duration – возвращает то, что есть, с подсказкой, что приедет больше.

Включают это поведение два условия. Первое – мультивариантный плейлист (или сам медиа-плейлист в более старых конфигах) рекламирует готовность сервера блокировать запрос:

#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=0.7,HOLD-BACK=6

CAN-BLOCK-RELOAD=YES – однозначный сигнал, что long-poll-запросы с _HLS_msn обрабатываются. PART-HOLD-BACK – минимальное расстояние, в секундах, от live edge, с которого плеер должен начинать воспроизведение; обычно 3× PART-TARGET. HOLD-BACK – segment-level fallback для клиентов, которые не поддерживают parts; шесть секунд – рекомендованный спецификацией пол.

Второе условие – сервер действительно реализует long-poll. Именно здесь коммерческие сборки тихо разваливаются: многие origin-сервера сразу возвращают то, что лежит на диске, игнорируя _HLS_msn, и полагаются на то, что плеер повторит запрос. Итог – polling-цикл, наряженный в LL-HLS, и бюджет задержки возвращается к обычному HLS. Apple Authoring Specification требует, чтобы сервер, рекламирующий CAN-BLOCK-RELOAD=YES, реально блокировал. Прогоните сквозную проверку через mediastreamvalidator и Apple hlsreport, прежде чем объявить деплой совместимым.

Механизм 4 – Отчёты по рендициям (`#EXT-X-RENDITION-REPORT`)

Плеер, который хочет переключиться с 720p на 1080p посреди прямой трансляции, не может использовать состояние blocking-reload плейлиста 720p, чтобы узнать, где сейчас 1080p. Без подсказки ему пришлось бы выпустить новый GET к медиа-плейлисту 1080p, распарсить его, найти последний sequence/part – и потом запустить long-poll. Этот round-trip стоит сотни миллисекунд и часто даёт односегментный ребуферинг на границе переключения.

Rendition-отчёты исключают эту дыру, прицепляя текущее состояние каждой соседней рендиции к каждому медиа-плейлисту:

#EXT-X-RENDITION-REPORT:URI="../1080p/playlist.m3u8",LAST-MSN=42,LAST-PART=8
#EXT-X-RENDITION-REPORT:URI="../480p/playlist.m3u8",LAST-MSN=42,LAST-PART=8
#EXT-X-RENDITION-REPORT:URI="../360p/playlist.m3u8",LAST-MSN=42,LAST-PART=8

Каждый отчёт перечисляет URI соседнего медиа-плейлиста, его последний sequence number и индекс part'а. Плеер, переходящий на 1080p, читает rendition-отчёт по 1080p прямо из плейлиста 720p, который у него уже есть, и тут же отправляет playlist.m3u8?_HLS_msn=42&_HLS_part=9 к 1080p – без зондирующего round-trip, без ребуферинга.

Apple Authoring Specification требует, чтобы каждая LL-HLS-рендиция рекламировала rendition-отчёты для всех соседних видео-, аудио- и субтитровых рендиций той же группы вариантов. В стриме с 5 видео-рендициями, 3 аудио и 2 субтитрами каждый медиа-плейлист несёт 9 строк rendition-отчётов. Это полоса, которую плеер платит один раз с плейлистом, а не 9 дополнительных long-poll'ов на каждое переключение.

Механизм 5 – Дельта-обновления плейлиста (`CAN-SKIP-UNTIL` + `#EXT-X-SKIP`)

Пятый механизм – то, что не даёт самому плейлисту расти бесконтрольно. Плейлисты обычного HLS для прямых трансляций обычно держат скользящее окно из нескольких сотен секунд – за его пределами сегменты выпадают из нижней части файла, и тот остаётся компактным. LL-HLS делает плейлист гораздо более болтливым: каждое обновление публикует ещё и parts снизу, а скользящее окно приходится держать длиннее, если плеер должен уметь перематывать назад или восстанавливаться после долгого ребуферинга. Без вмешательства медиа-плейлист LL-HLS вырастает до десятков килобайт на обновление, а при 25 обновлениях в секунду на рендицию счёт за полосу становится серьёзным.

Дельта-обновления решают это. Сервер рекламирует границу пропуска – самый старый сегмент, который плеер может пропустить, – в #EXT-X-SERVER-CONTROL:

#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,CAN-SKIP-UNTIL=36.0,PART-HOLD-BACK=0.7

CAN-SKIP-UNTIL=36.0 означает, что сервер может опустить любой сегмент старше 36 секунд от live edge. Apple Authoring Specification требует CAN-SKIP-UNTIL ≥ 6× target-duration; для 6-секундного target пол – 36 с. Плеер включается, добавляя _HLS_skip=YES к своему long-poll URL:

GET /720p/playlist.m3u8?_HLS_msn=42&_HLS_part=7&_HLS_skip=YES HTTP/1.1

Сервер возвращает плейлист с новым тегом вместо пропущенных сегментов:

#EXT-X-SKIP:SKIPPED-SEGMENTS=80

Это значит «следующие 80 сегментов я опустил – у тебя они уже есть с предыдущего ответа». Плеер вклеивает пропущенный диапазон в своё представление плейлиста, и объём данных на проводе сокращается на порядок. Семантика эквивалентна полному плейлисту; меняются только байты в эфире.

CMAF и init-сегменты EXT-X-MAP никогда не пропускаются, потому что плееру они могут понадобиться в любой момент. Discontinuities и date-range'ы, попавшие в пропущенное окно, тоже не пропускаются – сервер обязан их вернуть. Реализации иногда ошибаются в обработке discontinuity – в issue-трекере hls.js с конца 2025 года висит регрессия, в которой дельта-обновления роняли discontinuity, нужную плееру для согласования рекламных маркеров. Прогоняйте mediastreamvalidator -P и Apple hlsreport после каждого мажорного обновления пакетизатора.

Рис. 2. Long-poll-цикл blocking-reload. Плеер просит «плейлист, когда part 7 сегмента 42 появится»; сервер удерживает запрос; кодировщик публикует part 7; сервер мгновенно отдаёт ответ; плеер запускает следующий long-poll до парсинга предыдущего.

Реальный LL-HLS медиа-плейлист, строка за строкой

Вот реалистичный LL-HLS медиа-плейлист одного варианта, посреди трансляции: один сегмент закрыт полностью, следующий – в процессе.

#EXTM3U
#EXT-X-VERSION:9
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:41
#EXT-X-PART-INF:PART-TARGET=0.2
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,CAN-SKIP-UNTIL=36.0,PART-HOLD-BACK=0.7,HOLD-BACK=6
#EXT-X-MAP:URI="init.mp4"

#EXTINF:6.000,
seg41.m4s

#EXT-X-PART:DURATION=0.20000,URI="seg42-p1.m4s",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.20000,URI="seg42-p2.m4s"
#EXT-X-PART:DURATION=0.20000,URI="seg42-p3.m4s"
#EXT-X-PART:DURATION=0.20000,URI="seg42-p4.m4s"
#EXT-X-PART:DURATION=0.20000,URI="seg42-p5.m4s"
#EXT-X-PART:DURATION=0.20000,URI="seg42-p6.m4s"
#EXT-X-PART:DURATION=0.20000,URI="seg42-p7.m4s"

#EXT-X-PRELOAD-HINT:TYPE=PART,URI="seg42-p8.m4s"

#EXT-X-RENDITION-REPORT:URI="../1080p/playlist.m3u8",LAST-MSN=42,LAST-PART=7
#EXT-X-RENDITION-REPORT:URI="../480p/playlist.m3u8",LAST-MSN=42,LAST-PART=7
#EXT-X-RENDITION-REPORT:URI="../360p/playlist.m3u8",LAST-MSN=42,LAST-PART=7

Читайте сверху вниз. #EXTM3U объявляет HLS. #EXT-X-VERSION:9 – минимальная версия HLS, которую плеер должен поддерживать; версия 9 – первая, в которой полный набор LL-HLS-тегов, включая CAN-SKIP-UNTIL и #EXT-X-SKIP; для исходного LL-HLS 2019 без дельта-обновлений хватало версии 6. #EXT-X-TARGETDURATION:6 – никакой сегмент не превышает 6 секунд. #EXT-X-MEDIA-SEQUENCE:41 – сегмент 41 – первый закрытый сегмент в этом срезе плейлиста. #EXT-X-PART-INF:PART-TARGET=0.2 объявляет целевую длительность part. #EXT-X-SERVER-CONTROL рекламирует long-poll-возможность, границу пропуска, part-уровневый и сегмент-уровневый hold-back. #EXT-X-MAP указывает на CMAF init-сегмент.

Затем закрытый сегмент: #EXTINF:6.000, и seg41.m4s. Затем 7 parts активного сегмента 42, каждый 200 мс, каждый со своим URI. Part 1 несёт INDEPENDENT=YES, потому что начинается с I-frame; остальные – P-frame parts, которые плеер сможет декодировать только после part 1.

Дальше preload-хинт: TYPE=PART и URI, по которому будет жить part 8, когда тот появится. Плеер уже сейчас отправляет HTTP GET по этому URI; сервер удерживает ответ до публикации part 8.

И наконец три rendition-отчёта для соседних 1080p, 480p и 360p. Каждый сообщает последний sequence number и part-индекс, которого достигла соответствующая рендиция; ABR-переключение может long-poll'ить новый вариант напрямую, без зондирующего round-trip'а.

Когда плейлист обновится, сегмент 42 закроется (#EXTINF:6.000, + seg42.m4s), снизу появятся первые parts сегмента 43, preload-хинт сместится на part 1 сегмента 43, а rendition-отчёты продвинутся. Старые сегменты выпадут из нижней части файла по мере сдвига окна. С _HLS_skip=YES сервер вернёт новое состояние минус #EXT-X-SKIP:SKIPPED-SEGMENTS=N вместо сегментов, которые плеер уже видел.

Рис. 3. Тот же плейлист с аннотацией каждого тега. Линия между закрытыми сегментами и in-progress parts – это live edge.

Chunked transfer, версии HTTP и что происходит на проводе

LL-HLS делает long-poll поверх обычного HTTP. Исходный дизайн 2019 года полагался на HTTP/2 server push для доставки parts; этого требования больше нет. В 2026 расклад в продакшене примерно такой: около половины – HTTP/1.1 chunked transfer encoding, треть – HTTP/2-стримы, остальное – HTTP/3 поверх QUIC, причём HTTP/3 растёт быстрее всех, потому что естественно ложится на тот же QUIC-стек, что лежит в основе MoQ.

Механика на проводе чуть отличается по версиям HTTP, но семантика одинаковая. По HTTP/1.1 сервер удерживает long-poll, затем начинает отвечать с Transfer-Encoding: chunked и пишет каждый фрейм part'а отдельным chunk'ом по мере того, как кодировщик его выдаёт. По HTTP/2 и HTTP/3 сервер открывает стрим и пишет data-фреймы по мере их появления; оба протокола поддерживают то же поведение, потому что оба относятся к телу ответа как к последовательности фреймов, а не как к единому буферу. Смысл – один и тот же во всех версиях – плеер начинает декодировать первый байт part'а, как только тот пришёл, до того, как part целиком будет передан.

CMAF-чанки – это encoding-side-аналог LL-HLS-parts. CMAF-чанк – это минимальная независимо декодируемая единица, которую может произвести CMAF-кодировщик; на практике пакетизаторы делают один чанк на part, и в продакшен-разговорах слова «chunk» и «part» используются как синонимы. Полная цепочка: кодировщик выдаёт CMAF-chunk → пакетизатор оборачивает его как адресуемый HTTP-ресурс → медиа-плейлист публикует #EXT-X-PART, указывающий на него → сервер доставляет через chunked transfer или HTTP/2/3-стримы → плеер декодирует, как только пришёл первый кадр. Современные пакетизаторы – Wowza, AWS MediaPackage, Bitmovin Live, Mux Live, Norsk, Unified Streaming, Shaka Packager – выпускают LL-HLS как CMAF с одним чанком на part по умолчанию. Формат CMAF определён в ISO/IEC 23000-19:2024; рекомендации LL-HLS-over-CMAF – в Apple Authoring Specification §3 и DASH-IF Low-Latency Modes For DASH.

ABR в LL-HLS: как плеер переключается

ABR-переключение в обычном HLS прямолинейно: когда плеер решает уйти с 720p на 1080p, он забирает медиа-плейлист нового варианта, находит границу сегмента, скачивает новый init-сегмент, если тот изменился, и продолжает. В LL-HLS та же машина, но добавляются два ограничения.

Во-первых, плеер может переключиться только на независимом part'е – том, чья строка #EXT-X-PART несёт INDEPENDENT=YES. Независимые parts начинаются с I-frame и могут декодироваться без отсылки к предыдущим parts. Apple Authoring Specification рекомендует не реже одного независимого part'а в секунду; это даёт плееру точку переключения каждые 5 parts при PART-TARGET=0.2. P-frame parts не могут быть первым part'ом, который плеер декодирует на новом варианте.

Во-вторых, плеер использует rendition-отчёты, чтобы пропустить зондирующий round-trip. Имея rendition-отчёты, последовательность переключения такая: оценщик полосы решает уйти с 720p на 1080p; плеер читает rendition-отчёт по 1080p из текущего плейлиста 720p (LAST-MSN=42, LAST-PART=7); плеер сразу выпускает long-poll к плейлисту 1080p за _HLS_msn=42&_HLS_part=8; когда ответ приходит – скачивает init-сегмент 1080p, если того нет в кеше, и начинает забирать parts. Никакого лишнего зондирования, никакого GET'а плейлиста, который возвращает «вы уже синхронны», никакой паузы на границе сегмента.

Угловой случай – когда 1080p и 720p разъехались по времени относительно друг друга. CMAF задаёт временное выравнивание рендиций, так что для заданного media-sequence-number соответствует один и тот же момент реального времени в каждой рендиции. Apple Authoring Specification §2.10 делает это выравнивание обязательным для вариантов одного мультивариантного плейлиста; если upstream-кодировщик дрейфует, ABR-переключения будут давать видимые глитчи. Сверяйте выравнивание через mediastreamvalidator после каждого изменения конфигурации кодировщика.

Где сейчас реально находится пол задержки, 2026

Продакшен-сборки Mux, Wowza, Bitmovin и AWS, которые сегодня доставляют LL-HLS, сообщают о поле в 2–3 секунды glass-to-glass для зрителей в том же регионе на современных устройствах. Демонстрации Apple на WWDC 2025 показывали суб-2-секундные задержки в контролируемой среде на iPhone 15 / iOS 17 с chunked-CMAF-origin за edge-сетью iCloud. Среднее в проде слегка выше – 3–5 с – потому что вклады, которые LL-HLS не покрывает (глубина пайплайна кодировщика, предзагрузка декодера, last-mile-джиттер), никуда не исчезают.

Цифры конкретны. Опубликованный замер Mux 2024 года показывает у LL-HLS 4.0 с среднего glass-to-glass на 35 000 сессий (медиана 3.2 с; p95 6.1 с; p99 9.4 с) против 22.0 с среднего у обычного HLS в тех же условиях. Замеры Wowza 2025 – 5–6 с для одного клиента на Safari iPhone. Bitmovin сообщает о 3–4 с в продакшене своих заказчиков с нижней границей 1.5 с, достижимой только при тонкой настройке пайплайна (B-frames выключены, GOP = 1 секунда, сегмент 2 секунды и parts 200 мс). Spec-grade-реализации Apple на Apple TV / iOS попадают ближе к 2 с, когда origin находится в одном сетевом регионе с клиентом.

Причина, по которой продакшен-среднее живёт выше 2 секунд, – LL-HLS не может сделать кодировщик быстрее, чем его пайплайн, декодер быстрее, чем его предзагрузка, или сеть быстрее, чем RTT × джиттер. Две секунды, которые срезает LL-HLS, – это вклады обновления плейлиста и буфера, которые никогда не упирались в физику. Оставшиеся 2–3 секунды – реальный пол: 1 с пайплайна кодировщика (GOP, буферизуемый для B-frame-референса, плюс CABAC-энтропия плюс мукс чанка), 200–400 мс предзагрузки декодера, 100–300 мс last-mile-RTT, 200–500 мс оверхеда CDN/origin. Ниже 2 секунд нужен WebRTC – и за это придётся заплатить CDN-экономикой, которая делает HLS дешёвым в эксплуатации.

Типичные грабли

Пять механизмов описывают, что такое LL-HLS. Грабли описывают, что в проде ломается, когда один из механизмов настроен неправильно. Каждая команда, поставляющая LL-HLS, наступает на их подмножество.

«Грабли – origin отвечает мгновенно и игнорирует _HLS_msn. Origin рекламирует CAN-BLOCK-RELOAD=YES, но фактически long-poll не реализует. Плеер опрашивает; сервер возвращает текущий плейлист; плеер опрашивает снова; задержка остаётся на уровне обычного HLS, хотя плейлист выглядит LL-HLS-совместимым. Проверка – выпустить запрос с _HLS_msn, выставленным существенно впереди live edge, и засечь, сколько сервер его держит. Ответ должен прийти в пределах ±50 мс от момента публикации запрошенного part'а, а не в пределах poll-интервала плеера.»
«Грабли – PART-TARGET выставлен слишком высоко. Некоторые пакетизаторы по умолчанию ставят PART-TARGET в 1000 мс или 500 мс, чтобы разгрузить origin. Пол задержки сдвигается соответственно: PART-HOLD-BACK – это 3× PART-TARGET, поэтому секундный part даёт минимум 3 с обязательного hold-back поверх пола кодировщика и декодера. Продакшен LL-HLS использует 200-мс parts; 500 мс – это регрессия от дефолта.»
«Грабли – слишком редкие независимые parts. Apple Authoring Specification рекомендует один независимый part на каждую секунду реального времени. Часть кодировщиков по умолчанию ставит один на сегмент (раз в 6 секунд), что заставляет ABR-переключения ждать границы сегмента; задержка на переключении становится целым сегментом, а не одним part'ом. Настройте кодировщик на I-frame раз в секунду; проверяйте ffprobe -show_packets после смены конфигурации.»
«Грабли – CDN cache-key игнорирует _HLS_msn / _HLS_part. CDN, кеширующий плейлист без этих query-параметров в ключе, отдаёт устаревший плейлист на каждый long-poll. Фикс – включить _HLS_msn, _HLS_part и _HLS_skip в cache-key CDN и выставить плейлисту Cache-Control: max-age=1 (или 0), чтобы CDN перетягивал на каждом запросе. Akamai, Cloudflare, Fastly и AWS CloudFront – все поддерживают, но это нужно настраивать явно на каждый деплой.»
«Грабли – discontinuity, выпавшая из дельта-обновления. Когда сервер возвращает #EXT-X-SKIP:SKIPPED-SEGMENTS=N, любые #EXT-X-DISCONTINUITY или #EXT-X-DATERANGE (рекламный маркер SCTE-35) внутри пропущенного диапазона должны остаться в ответе. Некоторые версии пакетизаторов их тихо роняют. В трекере hls.js с конца 2025 года висит регрессия ровно об этом. Прогоняйте mediastreamvalidator -P после каждого обновления пакетизатора.»
«Грабли – старый плеер опрашивает плейлист без _HLS_msn. Legacy-плеер, не умеющий LL-HLS, выпускает обычный GET к тому же URL медиа-плейлиста. Сервер должен по-прежнему вернуть валидный HLS-плейлист – LL-HLS-расширения должны деградировать корректно, и плеер просто не видит LL-HLS-теги. Но многие серверы заворачивают плейлист в CMAF-chunked-ответ, который сбивает с толку не-LL-HLS-плееры; проверяйте mediastreamvalidator'ом против обоих профилей до релиза.»
«Грабли – длительности EXT-X-PART не суммируются в #EXTINF при закрытии сегмента. Спецификация требует, чтобы сумма длительностей parts точно равнялась #EXTINF сегмента с точностью до микросекунды. Дрейф кодировщика даёт 5.9999 против 6.0000, который одни плееры принимают, а другие отвергают; в проде заставьте кодировщик выдавать точные длительности и сверяйте mediastreamvalidator'ом.»

Когда LL-HLS – правильный выбор, а когда – нет

LL-HLS – правильный протокол, когда вы хотите CDN-экономику HLS, поставляете контент на устройства Apple и согласны на 2–5 секунд задержки. Это покрывает большинство live OTT-кейсов выше 100 000 одновременных зрителей – прямые трансляции спорта без интеграции со ставками, новостной эфир, концерты, live-shopping, где чат – основное взаимодействие, классы с отложенными Q&A, просмотр записей видеонаблюдения.

LL-HLS – неправильный выбор в двух направлениях. Вверх – когда нужна задержка ниже двух секунд (ставки на спорт, прямые аукционы, real-time-игры, телемедицина с синхронным видео, видеоконференции) – ответом будут WebRTC delivery и Media over QUIC, а LL-HLS оставит видимую секунду лага между действием и реакцией. Вниз – когда задержка выше 10 секунд не страшна (длинные VOD, повторы, видео-подкасты, архив) – простой HLS или MPEG-DASH проще, дешевле и дружелюбнее к кешу. LL-HLS платит за CDN объёмом long-poll-запросов; если задержка не нужна, не нужны и расходы.

Не-Apple-альтернатива в той же полосе 2–5 секунд – LL-DASH поверх chunked-CMAF – те же CMAF-чанки, произведённые и упакованные один раз, отдаваемые как HLS Apple-устройствам и как DASH всему остальному. CMAF делает это практичным без удвоения хранилища; рекомендуемый кластерным брифом паттерн на 2026 – «LL-HLS для iOS/Safari, LL-DASH для Chrome/Edge/Firefox/Android, один набор CMAF-чанков под обоими». Дерево семейства протоколов показывает, где сидит каждый протокол.

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

Мы поставляли LL-HLS в платформы прямого e-learning, OTT-сервисы, телемедицинские системы триажа и live-shopping. Наша инженерная команда стриминга относится к пяти механизмам выше как к единой системе – пайплайн кодировщика, выдача пакетизатора, поведение long-poll на origin, cache-key'и CDN и ABR-алгоритм плеера настраиваются вместе, потому что продакшен-формы отказа всегда живут на швах между слоями, а не внутри одного слоя. И обратное тоже: мы помогали заказчикам понять, что их кейс на самом деле требует WebRTC, и не уходить в LL-HLS под суб-секундный продукт. Честный скоупинг наперёд экономит три месяца «почему всё ещё буферит» потом.

Ключевые тезисы

  • LL-HLS заменяет цикл «опросил – подождал» обычного HLS пятью механизмами, которые превращают плейлист в long-poll-таргет, а сегмент – в последовательность 200-мс parts.
  • Пять механизмов – parts, preload-хинты, blocking reload, rendition reports, дельта-обновления – работают как система; уроните любой – поставите обычный HLS с лишними тегами.
  • HTTP/2 server push ушёл начиная с ревизии сентября 2023 Apple HLS Authoring Specification; preload-хинты его заменили.
  • Продакшен glass-to-glass-задержка в 2026 – 2–5 секунд; пол ниже 2 секунд требует WebRTC или Media over QUIC, не LL-HLS.
  • CMAF-чанки и LL-HLS-parts – одна и та же единица на проводе; один chunked-CMAF-origin может питать и LL-HLS, и LL-DASH.
  • Главная продакшен-форма отказа – origin, который рекламирует CAN-BLOCK-RELOAD=YES, но фактически не делает long-poll; сверяйте сквозно до объявления совместимости.

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

CTA

  • Поговорить с инженером по стримингу – про то, какая форма (LL-HLS, LL-DASH, WebRTC, MoQ) правильнее под вашу цель по задержке и бюджет на CDN.
  • Посмотреть наши кейсы – live-обучение, OTT, телемедицина, live-shopping.
  • СкачатьЧеклист готовности LL-HLS (2026) – 25 пунктов, которые каждая команда должна проверить, прежде чем объявить LL-HLS-сборку готовой к продакшену.

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

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