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

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

Коротко

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

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

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

Рисунок 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. Один и тот же дрейф, два ответа. Ресемплинг размазывает коррекцию по каждому сэмплу и остаётся беззвучным; отбрасывание целого кадра концентрирует её в одном слышимом разрыве.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Drop аудио на audio master. Если плеер делает drop аудио-кадров, чтобы следовать за видео, вместо drop видео-кадров, чтобы следовать за аудио, у него мастер наоборот. Аудио должно быть опорой; видео должно уступать. Перевёрнутый порядок даёт слышимые провалы аудио ради идеально гладкого видео, которого никто не просил.

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

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

Главное

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

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

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

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