Содержание статьи +
- Кратко
- Почему это важно
- Одна идея: мелкие кусочки, отданные заранее
- Почему аудио – неудобный пассажир
- Как LL-HLS несёт аудио: части и preload-подсказки
- Как LL-DASH несёт аудио: chunked CMAF
- CMAF-LL – общий фундамент под обоими
- Аудио – порог задержки, который никто не измеряет
- Разбор бюджета задержки
- Выбор кодека меняет размен
- Где здесь Фора Софт
- Главное
- Что почитать дальше
Опубликовано: 2026-06-06 · Время чтения: 18 мин · Автор: Николай Сапунов, CEO Фора Софт
Кратко
Стриминг с низкой задержкой сокращает паузу между событием перед камерой и тем, что вы видите дома, с обычных двадцати-тридцати секунд до двух-пяти, и делает это, нарезая каждый медиасегмент на куда более мелкие кусочки – их называют частями (parts) в LL-HLS от Apple и чанками (chunks) в chunked CMAF для LL-DASH, – которые плеер может забрать ещё до того, как сегмент дописан. Аудио здесь – тихий пассажир: оно кодируется фиксированными фреймами (1024 сэмпла, около 21 миллисекунды, для AAC; 20 миллисекунд для Opus), которые редко делятся нацело на эти доли-секундные части, поэтому упаковщик обязан решить, как разложить аудио по таймлайну низкой задержки. Ошибётесь здесь – и получите классические аудиобаги: звук отстаёт от картинки, пропадает на границах частей или поток тихо откатывается к высокой задержке, потому что аудио не успело. Эта статья объясняет, как аудио едет внутри LL-HLS, LL-DASH и CMAF-LL, почему аудио – обычно тот самый порог задержки, который никто не измеряет, и какие правила упаковки удерживают звук и картинку вместе на двух секундах задержки.
Почему это важно
Если вы транслируете живой спорт, аукционы, ставки, телешоу, торги или любое событие, где зрители сверяются в реальном времени – на телефоне, в браузере или на Smart TV, – задержка становится свойством продукта, и ваше аудио либо успевает за картинкой, либо нет. Эта статья – для продакт-менеджера, основателя или операционного руководителя без бэкграунда в стриминге, которому нужно понять, почему «низкозадержный» поток всё равно расходится, почему звук иногда щёлкает или пропадает в начале буфера, и как обсуждать починку с инженерами. К концу вы будете знать, что такое часть и чанк, почему размеры аудиофреймов воюют с доли-секундными частями и какие настройки упаковки решают, придёт ли звук вовремя. Каждый механизм восходит к управляющей спецификации – черновику RFC по HLS, ISO/IEC 23009-1 для DASH, ISO/IEC 23000-19 для CMAF и стандартам кодеков, – а не к блогу вендора.
Одна идея: мелкие кусочки, отданные заранее
Начнём с модели, на которой держится остальная статья. У обычного интернет-стриминга высокая задержка по скучной причине: плеер не начнёт медиасегмент, пока тот полностью не записан на сервере. Если сегменты по шесть секунд, самое свежее, что увидит зритель, уже минимум на шесть секунд старо ещё до начала загрузки, а буфер безопасности в два-три сегмента доводит реальную цифру до двадцати-тридцати секунд. Для фильма это нормально. Для гола, рёв соседей по которому вы слышите сквозь стену раньше, чем он появляется на экране, – нет.
Стриминг с низкой задержкой решает это одним ходом: нарезать каждый сегмент на куда более мелкие кусочки и позволить плееру забирать кусочек ровно в тот миг, когда его выдал кодер, пока остальной сегмент ещё пишется. Low-Latency HLS от Apple называет эти кусочки частями (parts); chunked CMAF, формат в основе Low-Latency DASH, называет их чанками (chunks). И те и другие – примерно 200–500 миллисекунд. Вместо шести секунд ожидания целого сегмента плеер получает свежее медиа несколько раз в секунду, и задержка glass-to-glass падает с двадцати-тридцати секунд обычного HLS или DASH до двух-пяти секунд, сопоставимых с кабельным ТВ.
Эта единственная идея – мелкие кусочки, отданные заранее – и есть вся игра. Всё остальное в статье вытекает из попыток уложить в такие мелкие кусочки именно аудио.
Почему аудио – неудобный пассажир
Видео и аудио кодируются по-разному, и именно эта разница делает низкозадержное аудио непростым.
Видеокодер гибок в выборе места фрейма. Кодер может разместить независимый фрейм – целую картинку, которую плеер декодирует без предыдущих кадров, – там, где попросит упаковщик, и части режутся по этим точкам. У аудио такой свободы нет. Аудиокодеки кодируют фиксированными фреймами, и фрейм – наименьшая единица, которую можно декодировать. Разрезать аудиофрейм пополам нельзя; отправлять надо целые фреймы.
Важна длительность фрейма, и она неудобна по самой своей природе. Самый распространённый стриминговый кодек, AAC-LC, кодирует 1024 сэмпла на фрейм. При стандартной видеочастоте дискретизации 48 000 сэмплов в секунду это:
1024 сэмпла ÷ 48 000 сэмплов в секунду = 0,021333… секунды
= 21,33 миллисекунды на один фрейм AACТо есть один фрейм AAC – около 21,3 миллисекунды. Opus, кодек WebRTC и всё более частый выбор в стриминге, по умолчанию использует фреймы по 20 миллисекунд. Ни одно из этих чисел не делится нацело на часть в 200, 300 или 500 миллисекунд. Часть в 300 миллисекунд вмещает 300 ÷ 21,33 ≈ 14,06 фрейма AAC – не целое число. Упаковщик не может отправить 14,06 фрейма, поэтому отправляет либо 14 (около 298,7 мс аудио), либо 15 (около 320 мс), и аудиочасть теперь чуть рассинхронизирована с видеочастью, которую должна сопровождать.
Отсюда и репутация низкозадержного аудио как чего-то капризного. Видеотаймлайн режется по аккуратным доли-секундным границам; аудиотаймлайн – по сетке в 21,33 миллисекунды, которая никогда с этими границами не совпадает. Кто-то – упаковщик – обязан согласовать две сетки, и то, как он это делает, определяет, останется ли аудио в синхроне.
Как LL-HLS несёт аудио: части и preload-подсказки
Apple представила Low-Latency HLS в 2020 году как расширение спецификации HLS, ныне отслеживаемое в черновике IETF draft-pantos-hls-rfc8216bis. Оно добавляет несколько тегов в медиаплейлист, и те же теги применимы к аудиорендициям, как и к видео.
Ключевой новый тег – EXT-X-PART, обозначающий частичный сегмент (partial segment). Если обычный HLS перечисляет целые сегменты через EXTINF, то LL-HLS дополнительно перечисляет части, из которых состоит сегмент, выпускаемый прямо сейчас, у каждой свой DURATION и URI. Аудиоплейлист рендиции получает собственные записи EXT-X-PART в том же ритме, что и видео, так что плеер забирает аудиочасти так же быстро, как видео.
Ещё два тега обеспечивают ритм. EXT-X-PRELOAD-HINT стоит в начале плейлиста и сообщает плееру URL следующей части до того, как она появилась, чтобы плеер успел отправить запрос; сервер держит запрос открытым и отдаёт байты «на скорости линии» в момент готовности части. EXT-X-SERVER-CONTROL объявляет, что сервер поддерживает эти блокирующие режимы, и задаёт PART-HOLD-BACK – на сколько плеер должен отступать от живого края, обычно тройная длительность части. Вместе они заменяют старый расточительный шаблон, когда плеер раз за разом опрашивал плейлист в надежде, что появилось новое медиа.
Конкретно для аудио один атрибут особенно полезен – INDEPENDENT. На видеочасти INDEPENDENT=YES означает, что часть начинается с независимого фрейма, с которого плеер может начать декодирование. Аудиофреймы по сути все декодируются независимо, но атрибут всё равно важен: плеер использует его, чтобы решить, где можно подключиться к потоку или чисто переключить рендицию. При переключении языка аудио или битрейта на ходу плеер ищет безопасную точку входа, и корректная разметка INDEPENDENT уберегает это переключение от паузы или щелчка.
Вот форма низкозадержного аудиоплейлиста рендиции (сокращённо):
#EXTM3U
#EXT-X-VERSION:9
#EXT-X-TARGETDURATION:4
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=1.0,CAN-SKIP-UNTIL=12.0
#EXT-X-PART-INF:PART-TARGET=0.33334
#EXT-X-MEDIA-SEQUENCE:200
# ...целые сегменты выше...
#EXTINF:4.00000,
seg200.mp4
# части сегмента, выпускаемого прямо сейчас:
#EXT-X-PART:DURATION=0.33334,URI="seg201.0.mp4",INDEPENDENT=YES
#EXT-X-PART:DURATION=0.33334,URI="seg201.1.mp4"
#EXT-X-PART:DURATION=0.33334,URI="seg201.2.mp4"
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="seg201.3.mp4"Обратите внимание: цель части – 0,33334 секунды, то есть 333 миллисекунды. В фреймах AAC это 333,34 ÷ 21,33 ≈ 15,6 фрейма – снова не целое. Собственная рекомендация Apple – выбрать длительность части, содержащую целое число аудиофреймов, либо смириться с тем, что длительности частей будут слегка плавать, чтобы каждая часть держала целое число фреймов. Такая вариация легальна – DURATION у каждого EXT-X-PART точно равен содержимому части, – но это значит, что аудиочасти и видеочасти не будут иметь одинаковую длительность, и ваши инструменты должны это спокойно переносить.
Как LL-DASH несёт аудио: chunked CMAF
MPEG-DASH достигает низкой задержки другим путём, который приходит туда же. Вместо нового понятия «часть» в манифесте Low-Latency DASH сохраняет обычный сегмент, но меняет то, как сегмент собирается и доставляется. Сегмент кодируется как последовательность чанков CMAF – каждый чанк это бокс moof (заголовок-метаданные) плюс бокс mdat (полезная нагрузка), несущий пару сотен миллисекунд медиа, – и сервер публикует каждый чанк по HTTP/1.1 chunked transfer encoding в момент его выпуска кодером. Плеер запрашивает сегмент открытым запросом и получает чанки потоком, проигрывая каждый по мере прихода, а не дожидаясь закрытия сегмента.
Задача манифеста – сообщить плееру, что чанки доступны раньше. Это делает атрибут availabilityTimeOffset (ATO) – десятичное число секунд, говорящее, насколько раньше номинального времени доступности сегмента можно забрать его первые байты. Если сегмент номинально становится доступен на четвёртой секунде, но его первый чанк готов на 3,6 секунды раньше, вы ставите availabilityTimeOffset="3.6", и низкозадержный плеер знает, что начинать тянуть сегмент можно почти сразу.
Аудио здесь получает особое обращение, и тут LL-DASH честен в том, что аудио – неудобный пассажир. Рекомендации DASH-IF по низкой задержке прямо разрешают аудио AdaptationSet работать с другим расписанием доступности, нежели видео: например, видео может агрессивно выставить availabilityTimeOffset, а аудио – более консервативно или вовсе без него. Причина – та самая проблема сетки фреймов: аудио не всегда может выдать чистый чанк на той же доли-секундной границе, что и видео, поэтому загонять аудио в агрессивное расписание видео рискует отправкой неполного или рассинхронизированного звука. Аудио на чуть более свободном расписании меняет несколько десятков миллисекунд задержки звука на надёжный звук без разрывов – почти всегда выгодный размен, ведь зрители куда легче прощают аудио, отстающее на волосок, чем аудио, которое щёлкает или пропадает.
CMAF-LL – общий фундамент под обоими
LL-HLS и LL-DASH можно обсуждать вместе именно потому, что оба стоят на одном медиаформате: CMAF, Common Media Application Format, стандартизованном как ISO/IEC 23000-19. CMAF задаёт боксы фрагментированного MP4, несущие медиа, а «низкозадержное» дополнение – это чанк CMAF, пара moof+mdat меньше полного фрагмента. Одна физическая библиотека чанков CMAF может адресоваться плейлистом LL-HLS (как части, через byte-range-запросы) и манифестом LL-DASH (как chunked-сегменты) одновременно.
Это сближение резко усилилось, когда Apple убрала исходное требование LL-HLS к HTTP/2 PUSH и заменила его моделью «preload-подсказка плюс блокировка». После этого клиент LL-HLS может выдать открытый byte-range-запрос к началу сегмента – «дай мне с байта 0 и продолжай слать по мере поступления», – что ведёт себя почти точно как chunked-transfer-поток, ожидаемый клиентом LL-DASH. Практический итог для аудио: вы кодируете и упаковываете аудио один раз, как чанки CMAF, и оба формата доставки могут его отдавать. Два отдельных аудиоконвейера держать не нужно.
Единственное правило, заставляющее это работать, – выравнивание: аудиочанки должны использовать границы, адресуемые обоими форматами. Выберите длительность сегмента, держащую целое число аудиофреймов, держите длительность чанка постоянной – и одни и те же аудиочанки служат и частям LL-HLS, и сегментам LL-DASH без переупаковки.
Аудио – порог задержки, который никто не измеряет
Вот часть, которую большинство низкозадержных проектов узнаёт на горьком опыте. Команды зацикливаются на видеозадержке – длительность части, размер чанка, поведение CDN – и считают аудио решённой мелочью. Затем поток выходит, аудио стабильно на такт позади картинки, и никто не может сказать почему. Ответ: аудио несёт задержку, которой нет у видеоконвейера, и эта задержка задаёт порог, который не убрать уменьшением частей.
Главный виновник – задержка кодера, она же priming. Чтобы стартовать чисто, кодер AAC добавляет в начало потока тихие «прайминговые» сэмплы – исторически 2112 сэмплов, выбранных потому, что такова была общая задержка кодеров среди существовавших реализаций AAC. Корректный декодер отбрасывает эти 2112 сэмплов при воспроизведении, но задержка, которую они представляют – около 44 миллисекунд при 48 кГц, – реальна и сидит в аудиотракте до того, как нарежется хоть одна часть. Разные кодеки несут разное: у Opus малая алгоритмическая задержка около 26,5 миллисекунды при фрейме по умолчанию 20 миллисекунд – отчасти поэтому он привлекателен, когда приоритет – задержка. Низкозадержный вариант AAC, AAC-ELD, был создан специально, чтобы сократить эту цифру для конференц-связи.
Второй виновник – сетка фреймов, с которой мы уже знакомы. Поскольку аудио отправляется целыми фреймами по 21,33 миллисекунды, аудиочасть округляется до границы фрейма, добавляя до одного фрейма зазора относительно видеочасти. Третий – буфер воспроизведения: плеер держит небольшой аудиобуфер, чтобы сгладить джиттер прихода, и на очень низких задержках, к которым стремится LL, этот буфер – главный регулятор баланса между стабильностью и задержкой.
Сложите всё – и структурный порог аудио (priming кодера плюс округление по сетке фреймов плюс минимальный буфер воспроизведения) обычно лежит в диапазоне 60–120 миллисекунд ещё до сетевой задержки. Это ниже порога, на котором большинство зрителей замечает ошибку синхронизации губ (примерно от 45 мс опережения звука до 125 мс отставания, по вещательным окнам допуска), но только если плеер корректно выравнивает презентационные метки времени. Когда аудио опаздывает, а плеер не компенсирует, вы выходите за окно, и зрителю кажется, что дубляж «сбит», хотя каждый отдельный компонент ведёт себя правильно.
«Частая ошибка – гнаться за видеозадержкой, игнорируя аудиопорог. Команда снижает длительность части с 500 мс до 200 мс, чтобы выиграть секунду видеозадержки, а затем заводит баг: аудио теперь отстаёт. Аудиопорог (priming + округление фреймов + буфер) не сжался вместе с частями; он просто стал большей долей общего – и вдруг заметен. Чинить надо не уменьшением аудиочастей (фрейм 21,33 мс не делится), а выравниванием презентационных меток времени и принятием того, что аудио идёт по более грубой сетке, чем видео.»
Разбор бюджета задержки
Поставим цифры. Допустим, цель – три секунды glass-to-glass на chunked CMAF, части по 333 миллисекунды и аудио AAC-LC на 48 кГц. Грубый аудиобюджет выглядит так:
| Этап | Задержка | Примечание |
|---|---|---|
| Priming кодера AAC | ~44 мс | 2112 сэмплов ÷ 48 000; отбрасывается при декодировании, но присутствует в тракте |
| Округление по сетке фреймов | до ~21 мс | один фрейм AAC зазора на границе части |
| Кодирование + упаковка | ~30–60 мс | lookahead кодера + сборка чанка |
| Сеть + CDN | ~100–300 мс | зависит от близости edge и поддержки chunked transfer |
| Буфер воспроизведения | ~150–400 мс | главный регулятор «стабильность против задержки» |
| Итого по аудиотракту | ~350–800 мс | до любого осознанного отступа от живого края |
Главная арифметика: priming кодера и сетка фреймов – вместе около 65 миллисекунд – заданы кодеком, а не размером части. Части и буферы можно уменьшать, а эти члены – нет. Поэтому «аудио – порог задержки»: неустранимые аудиочлены становятся пределом, к которому вы сходитесь, дотягивая всё остальное вниз.
Выбор кодека меняет размен
Выбранный кодек сдвигает порог. AAC-LC универсален и аппаратно декодируется везде, но несёт задержку priming в 2112 сэмплов и сетку фреймов в 21,33 миллисекунды. Opus, всё чаще поддерживаемый в CMAF и DASH, даёт меньшие фреймы (до 2,5 мс) и меньшую алгоритмическую задержку, поэтому позволяет срезать аудиопорог, если ваши целевые устройства его декодируют. Для вещательного иммерсивного звука AC-4 и MPEG-H несут собственные характеристики задержки и обычно оставлены для премиум-тиров, а не для пути с минимальной задержкой.
Практическое дерево решений короткое. Если нужно достать устройства Apple через нативный HLS – AAC безопасный выбор по умолчанию, и вы настраиваете вокруг его порога. Если вы контролируете плеер или нацелены на открытый веб с поддержкой Opus – Opus покупает вам меньший порог и меньшие фреймы. Если нужна сверхнизкая задержка ниже того, что даёт chunked-стриминг – субсекундная, разговорная, – вы вообще покинули территорию стриминга и должны смотреть на WebRTC, где аудиоконвейер построен именно под это.
Где здесь Фора Софт
Мы строим живое и интерактивное видео, где задержка – это продукт, а не приятный бонус: платформы спорта и ставок, аукционы и торговые приложения, e-learning с живым участием, телемедицина, где врачу и пациенту нужно чувствовать присутствие. В таких проектах аудиотракт – то место, где выигрывается или теряется бюджет синхронизации, и мы потратили реальные инженерные часы, согласуя сетку аудиофреймов с доли-секундными видеочастями, настраивая буферы воспроизведения для стабильности на двух секундах и выбирая между AAC и Opus под целевые устройства. Когда клиент говорит «низкозадержный поток расходится», ответ почти всегда в том, как аудио упаковали в части, а не в сети.
Главное
- Низкозадержный стриминг режет сегменты на доли-секундные части (LL-HLS) или чанки (LL-DASH); задержка падает до 2–5 с.
- Аудио идёт целыми фреймами – 21,33 мс у AAC, 20 мс у Opus, – которые редко делятся нацело на части.
- Выберите длительность сегмента, держащую целое число аудиофреймов, чтобы границы совпадали.
- LL-DASH позволяет аудио идти на своём availabilityTimeOffset, свободнее видео, чтобы избежать разрывов.
- Аудио несёт фиксированный порог – priming кодера плюс округление фреймов, – который не убрать настройкой.
- У Opus порог задержки ниже, чем у AAC; выбирайте кодек под целевую задержку и устройства.