Аудио в стриминге с низкой задержкой (LL-HLS, LL-DASH, CMAF-LL)

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

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

Кратко

Стриминг с низкой задержкой сокращает паузу между событием перед камерой и тем, что вы видите дома, с обычных двадцати-тридцати секунд до двух-пяти, и достигается это за счёт разбиения каждого медиасегмента на гораздо более мелкие части – их называют частями (parts) в LL-HLS (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 миллисекунд. Вместо ожидания целого сегмента в шесть секунд плеер получает свежие медиафрагменты несколько раз в секунду, и задержка «от экрана до экрана» падает с двадцати-тридцати секунд в обычном HLS или DASH до двух-пяти секунд – уровня, сопоставимого с кабельным телевидением.

Эта единственная идея – мелкие кусочки, отданные заранее – и есть суть игры. Всё остальное в статье вытекает из попыток уложить в такие мелкие кусочки именно аудио.

Рис. 1. При обычном стриминге плеер ждёт завершения всего сегмента; при низкозадержном – получает доли-секундные части по мере их готовности от кодера. Аудиодорожка синхронизирована с видео по одному и тому же таймингу.

Почему аудио – неудобный пассажир

Видео и аудио кодируются по-разному, и именно эта разница затрудняет реализацию низкозадержного аудио.

Видеокодер гибок в выборе места фрейма. Он может разместить независимый фрейм – целую картинку, которую плеер декодирует без опоры на предыдущие кадры, – в любом месте, указанном упаковщиком, и сегменты разрезают по этим точкам. У аудио такой свободы нет. Аудиокодеки работают с фиксированными фреймами, и фрейм – это наименьшая единица, которую можно декодировать. Разделить аудиофрейм пополам невозможно; передавать нужно только целые фреймы.

Важна длительность фрейма, и она по своей природе неудобна. Самый распространённый стриминговый кодек – 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 миллисекунды, которая никогда не совпадает с этими границами. Кто-то – упаковщик – должен согласовать две сетки, и именно способ, которым он это делает, определяет, останется ли аудио в синхроне.

Рис. 2. Фреймы AAC – по 21,33 мс; в интервале 300 мс помещается 14,06 фрейма. Упаковщик округляет до целого числа фреймов, поэтому границы аудио и видео расходятся, если длительность сегмента не подобрана так, чтобы аудиофреймы укладывались ровно.

Как LL-HLS передаёт аудио: части и подсказки предзагрузки

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 – выбрать длительность части, содержащую целое число аудиофреймов, либо смириться с тем, что длительность частей будет слегка варьироваться, чтобы каждая часть содержала целое число фреймов. Такая вариация допустима – у каждого EXT-X-PART значение DURATION точно соответствует содержимому части, – но это означает, что длительность аудиочастей и видеочастей может не совпадать, и ваши инструменты должны корректно с этим работать.

Как 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, а аудио – более консервативно или вовсе без него. Причина – та самая проблема сетки фреймов: аудио не всегда может выдать чистый чанк на той же доли-секундной границе, что и видео, поэтому загонять аудио в агрессивное расписание видео рискует отправкой неполного или рассинхронизированного звука. Аудио на чуть более свободном расписании меняет несколько десятков миллисекунд задержки звука на надёжный звук без разрывов – почти всегда выгодный размен, ведь зрители куда легче прощают аудио, отстающее на волосок, чем аудио, которое щёлкает или пропадает.

Рис. 3. В LL-DASH сегмент собирается из чанков CMAF (moof + mdat) и передаётся по мере поступления каждого. Аудио может использовать собственный 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 и заменила его моделью «подсказка предзагрузки плюс блокировка». После этого клиент LL-HLS может отправить открытый запрос на диапазон байтов с начала сегмента – «дай мне с байта 0 и продолжай отправлять по мере поступления» – что ведёт себя почти так же, как ожидаемый клиентом 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, этот буфер становится главным регулятором баланса между стабильностью и задержкой.

Сложите всё – и структурный порог аудио (подготовка кодера, округление по сетке кадров и минимальный буфер воспроизведения) обычно составляет 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; выбирайте кодек в зависимости от целевой задержки и используемых устройств.

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

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

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