Коррекция дрейфа: ресемплинг, drop и вставка кадров

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

Коротко

Два устройства в любом медиаконвейере работают на разных кварцевых генераторах, поэтому сторона, захватывающая аудио, и сторона, его воспроизводящая, никогда не синхронизированы по частоте – крошечная разница со временем накапливается и превращается в слышимый дрейф во время звонка или стрима. Существует два подхода к исправлению: мягкий – непрерывный ресемплинг, при котором добавляется или убирается доля сэмпла за раз, оставаясь при этом неслышимым; и жёсткий – отбрасывание (drop) или вставка целых кадров, что проще в реализации, но вызывает щелчки, провалы и заикания. Хорошо спроектированная система корректирует дрейф непрерывно и незаметно; плохо спроектированная ждёт, пока буфер опустеет или переполнится, и в этот момент «дергает» слушателя. В этой статье объясняется арифметика дрейфа, разбираются стратегии коррекции на реальных числах и показывается, почему выбор между ними определяет разницу между продуктом, звучащим профессионально, и тем, что порождает тикеты в службу поддержки.

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

Если вы работаете с конференциями, стримингом, OTT, видеонаблюдением или телемедициной, жалоба «сначала звук нормальный, но через двадцать минут начинает "уплывать"» – одна из самых частых и при этом самых неправильно диагностируемых. Это почти никогда не ошибка кодека и не проблема сети; скорее всего, речь идёт о двух часах, идущих с немного разной скоростью, и приёмнике, который грубо справляется с рассогласованием. Продакт-менеджер, прочитав это, поймёт, почему симптомы проявляются со временем и в чём суть инженерного компромисса. Инженер получит чёткую карту: когда стоит применять ресемплинг, а когда допустимы потеря кадров или их вставка. Весь смысл коррекции дрейфа в том, что пользователь её не замечает – поэтому те, кто её проектирует, должны понимать её глубоко.

Рисунок 1. Двое независимых часов, один буфер между ними. Когда производитель тикает хоть немного быстрее потребителя, буфер медленно наполняется, пока что-то не сломается.

Корень проблемы: двое часов, которые никогда не совпадают

Начните с одного факта, который объясняет всё остальное. Каждое устройство, захватывающее или воспроизводящее аудио, отсчитывает время с помощью крошечного кварцевого кристалла, вибрирующего с фиксированной частотой. Этот кристалл управляет аналого-цифровым преобразователем на стороне записи и цифро-аналоговым – на стороне воспроизведения. Проблема в том, что не бывает двух абсолютно одинаковых кристаллов. Кристалл с номиналом 48 000 Гц на одном устройстве может работать на частоте 48 000,5 Гц, а на другом – на 47 999,5 Гц. Оба – в пределах допуска; ни один не неисправен. Они просто не одинаковы.

Величина рассогласования измеряется в частях на миллион – ppm. Одна часть на миллион означает одну ошибку на каждый миллион тактов. Потребительские кристаллы обычно имеют погрешность ±20…±50 ppm, поэтому в худшем случае два обычных устройства могут отличаться друг от друга на 100 ppm. На первый взгляд это ничтожно мало, и в пересчёте на секунду так оно и есть. Но за время совещания или просмотра фильма разница становится заметной.

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

Разбор на числах: насколько быстро накапливается дрейф?

Числа делают это наглядным, поэтому приведём реальную арифметику на странице. Допустим, часы захвата и часы воспроизведения различаются на 50 частей на миллион – вполне реалистичная цифра для двух потребительских устройств.

Шаг один – перевести 50 ppm в долю. Пятьдесят частей на миллион – это 50, делённое на миллион:

50 ppm = 50 / 1 000 000 = 0,00005

Шаг два – определить, сколько времени двое часов набирают или теряют друг относительно друга за секунду воспроизведения:

0,00005 × 1 секунда = 0,00005 секунды = 0,05 миллисекунды в секунду

Шаг три – накопить за часовой звонок, то есть за 3600 секунд:

0,05 мс/секунду × 3600 секунд = 180 миллисекунд

Итак, через час аудио и видио расходятся на 180 миллисекунд. Теперь сравните это с допуском lip-sync, который реально навязывает человеческий глаз. ITU-R BT.1359-1 ставит порог приемлемости на уровне «аудио опережает видео на 90 мс» или «отстаёт на 185 мс»; за этим зрители считают синхронизацию неприемлемой. Нескорректированное рассогласование в 50 ppm выходит ровно на этот край к концу часового звонка. Переведите тот же дрейф в сырые сэмплы – и давление на буфер станет очевидным: при частоте дискретизации 48 000 Гц каждые 0,00005 секунды означают, что буфер набирает или теряет 2,4 сэмпла в секунду – около 8640 сэмплов, или 180 мс аудио, за час. Что-то должно поглотить эти сэмплы, и это что-то – коррекция дрейфа.

Мягкий вариант: непрерывный ресемплинг

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

Идея проста, даже если математика – нет. Ресемплинг, или преобразование частоты дискретизации, берёт поток сэмплов, полученных с одной частотой, и реконструирует, как тот же звук выглядел бы при дискретизации с немного другой частотой. Чтобы скорректировать дрейф в 50 ppm, когда сторона воспроизведения работает слишком медленно, приёмник ресемплирует входящее аудио с 48 000 Гц примерно до 48 002,4 Гц – добавляя около 2,4 сэмпла в секунду, равномерно распределённых по сигналу, – так что воспроизведение опустошает буфер с той же скоростью, с какой отправитель его наполняет.

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

Есть и вторая, родственная техника, которую стоит знать: её применяют внутри движков реального времени, где запуск полного ресемплера на каждом блоке слишком затратен – это растяжение во времени с сохранением высоты тона. Буфер джиттера в WebRTC, NetEQ, как раз использует такой подход. Когда в буфере накапливается больше аудиоданных, чем позволяет целевая задержка, запускается операция Accelerate, ускоряющая воспроизведение блока; когда буфер почти пуст – операция PreemptiveExpand, удлиняющая его. Обе операции опираются на общий алгоритм растяжения во времени, который сжимает или растягивает аудио по временной оси без изменения высоты тона, чтобы голос не звучал ни как у бурундука, ни как замедленная тянучка. На каждом цикле NetEQ сравнивает текущий уровень заполнения буфера с целевым и растягивает содержимое своего sync-буфера вверх или вниз, поддерживая уровень стабильным – это и есть коррекция дрейфа, применяемая крошечными непрерывными порциями. Механизм отличается от классического ресемплинга, но преследует ту же цель.

Рисунок 2. Один и тот же дрейф, два ответа. Ресемплинг распределяет коррекцию по всем сэмплам и остаётся незаметным; отбрасывание целого кадра концентрирует её в одном слышимом разрыве.

Жёсткий вариант: отбрасывание и вставка целых кадров

Грубый способ коррекции дрейфа – дождаться, пока буфер станет слишком полным или слишком пустым, и затем сразу удалить или продублировать целый кусок аудио. Когда буфер переполняется, приёмник отбрасывает кадр – например, выбрасывает 10 или 20 миллисекунд сэмплов. Когда буфер опустошается, он вставляет кадр – повторяет последний фрагмент или добавляет тишину, чтобы выиграть время.

Это работает в узком смысле: буфер остаётся в пределах допустимого. Но цена – в том, что слушатель это слышит. Удаление 10 миллисекунд непрерывной формы волны оставляет разрыв: сэмпл до обрезки и сэмпл после не стыкуются, и этот внезапный скачок сигнала звучит как щелчок или хлопок. Вставка повторённого фрагмента или отрезка тишины вызывает икоту или мгновенное заикание. Каждое исправление – небольшой, но слышимый дефект, а при дрейфе в 50 ppm система вынуждена делать это примерно раз в полсекунды. Из-за такой частоты дефекты накапливаются и начинают раздражать.

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

А что с видео? Drop и повтор – единственные варианты

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

Хорошая новость в том, что глаз гораздо снисходительнее к этому, чем ухо – к аудио-щелчку. Пропуск или повтор одного кадра из тридцати для большинства контента остаётся незамеченным – один кадр в 33 миллисекунды, показанный дважды или пропущенный, редко воспринимается зрительно. Эта асимметрия – ухо чувствительно, а глаз расслаблен – определяет доминирующую архитектуру синхронизации в медиаплеерах, называемую audio master. В этой архитектуре аудио воспроизводится со стабильной частотой и служит опорной временной шкалой; видео подстраивается под него путём отбрасывания или повторения целых кадров по мере необходимости. Люди замечают задержку в 10 миллисекунд в звуке гораздо легче, чем дублированный видеокадр, поэтому система приоритизирует качество аудио за счёт видео. Это разумный компромисс, и потому большинство плееров построены именно так.

Есть тонкость в современном стриминге. В плеере, работающем с HLS или DASH, аудио и видео – отдельные дорожки, декодируемые по общим часам воспроизведения, и плеер постоянно отслеживает текущую оценку дрейфа. Когда накопленный дрейф превышает порог, плеер либо корректирует временные метки и длительность кадра, чтобы он отображался в течение двух интервалов, либо дублирует кадр, если устройство работает слишком быстро, либо пропускает кадр, если оно отстаёт. Механизм остаётся тем же – логика «пропустить или повторить»; отличается лишь то, что у плеера есть все метаданные временных меток, чтобы точно определить момент действия.

Две стратегии в сравнении

Читайте таблицу по одной строке: каждая строка – одно измерение, по которому мягкий и жёсткий подходы различаются.

ИзмерениеМягкий: непрерывный ресемплингЖёсткий: drop / вставка кадра
Что меняетДолю сэмпла, каждый сэмплЦелый кадр (10–20 мс), изредка
СлышимостьНеслышимо при хорошем исполненииЩелчок, хлопок, провал или заикание
Когда действуетНепрерывно, упреждающеРеактивно, когда буфер у предела
Стоимость CPUВыше – настоящая работа DSPОчень низкая – копировать или выбросить
Работает для видео?Нет (кадры дискретны)Да – единственный вариант для видео
Типичный домКачественные аудио-движки, WebRTC NetEQОграниченные устройства, видео-дорожки
Режим отказаНичего слышимого; просто больше CPUСлышимые дефекты при сильном дрейфе

Честное резюме: ресемплинг – правильный выбор по умолчанию для аудио и то, что используют все качественные движки реального времени и стриминга; пропуск и вставка кадров неизбежны для видео и допустимы для аудио лишь на оборудовании, не способном позволить себе альтернативу, или когда дрейф настолько мал, что коррекции почти не срабатывают.

Особый случай: дрейф внутри подавления эха

Одно место, где дрейф часов особенно раздражает и удивляет инженеров, – акустическое подавление эха. Эхоподавитель работает, сравнивая аудиосигнал, отправленный в динамик, с тем, что записал микрофон, и вычитая первый из второго. Такое вычитание работает только при идеальном временном совпадении обоих потоков. Однако путь сигнала от динамика (render) и путь от микрофона (capture) могут использовать разные часы – и компенсация дрейфа может потребоваться даже тогда, когда захват и воспроизведение происходят на одном устройстве, если они работают на разных частотах дискретизации или используют отдельные часы кодека. Когда временные пути расходятся, опорный сигнал эхоподавителя теряет синхронизацию, и эхо, которое раньше полностью подавлялось, начинает просачиваться обратно в разговор. Решение – из той же категории: AEC непрерывно ресемплирует или перевыравнивает свой render-сигнал, чтобы он оставался привязанным ко времени захвата. Это коррекция дрейфа, скрытая внутри функции, которую большинство людей даже не считают связанной с проблемами синхронизации.

Частые ошибки, превращающие дрейф в баг

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

Ошибка «это наверняка сеть». Инженеры замечают, что аудио выходит из синхронизации, и сразу начинают анализировать пакеты и графики джиттера. Но джиттер искажает времена прибытия симметрично – он не создаёт устойчивого одностороннего сдвига. Признак дрейфа – именно эта устойчивость: синхронизация в начале нормальная, а затем ухудшается предсказуемо и постепенно. Медленное, монотонное сползание всегда указывает на рассогласование часов, а не на сетевой джиттер.

Только реактивный буфер. Приёмник, который никогда не пересемплирует и срабатывает лишь тогда, когда буфер становится пустым или полным, при любом реальном рассогласовании часов будет «щёлкать» или «заикаться» по фиксированному расписанию. Исправление – корректировать непрерывно и упреждающе, задолго до того, как буфер достигнет предела.

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

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

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

Мы разрабатываем решения для воспроизведения и синхронизации аудио в платформах конференций, OTT и интернет-ТВ, системах e-learning, телемедицинских приложениях и продуктах видеонаблюдения с 2005 года. Коррекция дрейфа – одна из самых незаметных, но при этом важнейших частей нашей работы: в продуктах реального времени мы настраиваем ресемплинг и поведение растяжения NetEQ так, чтобы длительные звонки проходили без слышимых артефактов; в стриминговых и OTT-плеерах – задаем пороги отбрасывания и повтора, чтобы шкала audio master оставалась стабильной на протяжении часов воспроизведения. Когда клиент сообщает: «звук уплывает со временем», мы сразу обращаемся к этому уровню – и почти всегда причина не в глубоком сбое, а в слишком грубой обработке рассогласования часов.

Главное

  • Два устройства работают на двух кристаллах; их небольшая разница в частотах со временем накапливается в дрейф.
  • Рассогласование в 50 ppm приводит к дрейфу около 180 мс за час.
  • Ресемплинг – мягкое исправление: сдвигает каждый сэмпл так, что изменение остаётся неслышимым.
  • Удаление или вставка кадра – жёсткое исправление: простое и дешёвое, но заметное на слух.
  • Видео может только удалять или повторять кадры; глаз прощает такие артефакты, поэтому синхронизация ведётся по аудио.
  • Постоянное одностороннее смещение – это дрейф часов, а не сетевой джиттер.

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

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

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