Маскировка потерь пакетов (PLC): как скрыть пропавшие кадры

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

Кратко

Когда звуковой пакет теряется в сети, получатель не может ждать – динамику нужен свежий фрагмент звука прямо сейчас, – поэтому вместо «дыры» система синтезирует правдоподобное продолжение. Это и называется маскировкой потерь пакетов, или PLC. Классический PLC повторяет и постепенно затухает последний хороший фрагмент звука, сохраняя его естественную высоту: такой подход хорошо скрывает одиночный пропущенный пакет, но при серии потерь приводит к роботизированному, «булькающему» звучанию. С 2020 года появилось новое поколение PLC на основе нейросетей – Google WaveNetEQ, deep PLC и Deep REDundancy (DRED) в Opus 1.5, – которые генерируют пропавший звук на основе обученной модели речи и остаются разборчивыми даже при очень высоких потерях. В статье простым языком разбираются четыре семейства PLC, объясняется, что делает NetEQ в WebRTC при потере пакета, чем PLC отличается от упреждающей коррекции ошибок и ретрансляции, а также какие метрики в продакшене покажут, сколько вашего звука «придумывается».

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

Если вы работаете с видеозвонками, телемедициной, контакт-центрами или онлайн-обучением, потеря пакетов – это не редкость, а норма для мобильных и домашних сетей, и именно это чаще всего замечают пользователи. Звук либо постепенно ухудшается, либо превращается в глючную роботизированную кашу, и разница между этими двумя сценариями почти полностью зависит от качества вашего PLC. Эта статья предназначена для продакт-менеджеров, основателей и операционных руководителей, которым важно понимать, на что способен PLC, а на что – нет: чтобы по диагностической метрике определить, связана ли жалоба «звук плохой» с эффектом маскировки или с проблемой выше по цепочке, и решить, оправданы ли затраты на внедрение более современного нейросетевого PLC. Даже senior-инженеры найдут здесь каждый факт, подтверждённый документацией WebRTC, профильными RFC, рекомендациями ITU или оригинальными научными исследованиями.

Проблема: представление должно продолжаться

Начнём с ограничения, которое и делает PLC необходимым. В реальном звонке динамик беспощаден: ему нужен свежий, непрерывный фрагмент звука – в WebRTC ровно 10 миллисекунд – для своевременного воспроизведения, 100 раз в секунду и бесконечно. Аудиоустройство не делает вежливую паузу, пока опоздавший пакет добирается по сети. Если следующий фрагмент звука не готов к моменту запроса, из динамика всё равно должно что-то прозвучать.

В реальной сети пакеты теряются постоянно: их отбрасывают перегруженные маршрутизаторы, они теряются на слабом Wi-Fi, повреждаются на сотовом участке или просто приходят слишком поздно, чтобы быть полезными. Для одностороннего стриминга это досадно, но поправимо – плеер может буферизовать несколько секунд и подождать. В живом разговоре так не получится: ждать – значит увеличивать задержку, а задержка свыше примерно 200 миллисекунд в одну сторону разрушает естественный диалог (этот «потолок» задержки мы разбираем в статье про WebRTC-конвейер звука). Поэтому при потере пакета у получателя остаются только плохие варианты, и PLC – наименее плохой из них.

Маскировка потерь пакетов – это семейство методов, которые заполняют пробел от пропавшего пакета синтезированным звуком, чтобы слушатель услышал что-то правдоподобное вместо пустоты. Название точное: PLC не восстанавливает потерянный звук – оригинал утрачен – он маскирует потерю, генерируя замену. Всё искусство в том, чтобы эта замена была достаточно близка к тому, что было сказано на самом деле, и слушатель ничего не заметил – или хотя бы не вздрогнул.

Самый простой ответ и почему от него отказались

Грубейший способ обработки потерь в PLC – это вставка нулей (zero insertion), или подстановка тишины: при потере пакета на эти миллисекунды воспроизводится тишина. Реализовать это тривиально, и это ничего не стоит.

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

Замещение формой волны: повторить недавнее прошлое

Следующая идея, на которой VoIP опирался два десятилетия, – это замещение формой волны (waveform substitution): вместо тишины пробелы заполняются фрагментом звука, который вы только что получили. Речь локально повторяется – например, протяжный гласный – это по сути одна и та же короткая форма волны, скопированная много раз, – поэтому недавний фрагмент обычно хорошо предсказывает, что будет дальше.

Простейший вариант просто повторяет последний полученный кадр. Более продвинутые версии, например стандартизированные в рекомендации ITU G.711 Appendix I, работают на уровне периода основного тона – длины одного цикла голосового сигнала, определяющей высоту звука. Вместо повторения произвольного фрагмента алгоритм находит самый недавний период основного тона и зацикливает его, чтобы синтезированный звук сохранял естественную высоту голоса, а не гудел на частоте кадров (ITU-T G.711 Appendix I, 1999). G.711 Appendix I намеренно прост – его опубликованная вычислительная сложность составляет около 0,5 MIPS на канал, что позволяет использовать его даже на самом простом телефонном оборудовании (ITU-T G.711 Appendix I, 1999).

Чтобы найти период основного тона, алгоритм сдвигает недавнюю форму волны относительно самой себя и ищет такой сдвиг, при котором совпадение будет максимальным – этот расчёт называется нормированной взаимной корреляцией. Сдвиг с наибольшей корреляцией определяет период основного тона, и именно этот фрагмент зацикливается (патентная литература по PLC с выравниванием формы волны, используется в Expand WebRTC).

Две доработки делают звук менее механическим. Во-первых, повторяемый фрагмент затухает по громкости тем сильнее, чем дольше длится пауза: слишком длинный цикл может превратиться в заметный гул. Затухание к тишине позволяет длинной потере плавно деградировать, а не вызывать ощущение гудения. Во-вторых, когда реальный звук наконец возвращается, синтезированный «хвост» и свежий реальный звук сшиваются плавным кроссфейдом, чтобы стык не сопровождался щелчком.

Замещение формой волны действительно хорошо работает при коротких изолированных потерях – один потерянный пакет в 20 миллисекунд обычно остаётся незамеченным. Его ограничения проявляются в двух случаях. Во-первых, оно не может воссоздавать новые звуки: если теряется пакет с началом нового слова, повтор и затухание предыдущего гласного дают искажённый звук, а не пропущенное слово. Во-вторых, метод быстро деградирует при серии (burst) последовательных потерь, поскольку каждый повторно воспроизводимый и затухающий фрагмент всё больше отклоняется от оригинала. Растяните один период основного тона на 200 миллисекунд – и получите тот самый «булькающий», роботизированный звук, знакомый по плохому звонку.

Рисунок 1. Четыре семейства маскировки потерь пакетов – от дешёвого и низкого по качеству метода вставки нулей до дорогих и эффективных нейросетевых подходов – на одном и том же пропавшем фрагменте.

Модельная маскировка: продление речи, а не формы волны

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

Именно так работает маскировка, встроенная в современные речевые кодеки. Слой SILK в кодеке Opus, который WebRTC использует для большей части голосового трафика, включает путь модельной маскировки, который декодер автоматически активирует при получении сигнала о потерянном кадре (IETF RFC 6716, §4.4, сентябрь 2012). Благодаря работе с речевой моделью он способен пережить более длительную потерю данных, чем простое замещение формы волны, прежде чем начнёт искажаться, и обеспечивает более естественный переход между звуками. Слова он, разумеется, не может придумать – продление сигнала в паузах даёт правдоподобное продолжение текущего звука, а не настоящий следующий слог, – но для одного и того же разговора это заметный шаг вперёд по качеству.

Нейросетевая маскировка: пусть сеть воссоздаст пропавший звук

Четвёртое семейство изменило само представление о возможном, и в продакшене оно появилось около 2020 года. Нейросетевой PLC заменяет вручную настроенные правила продления глубокой нейросетью, обученной на огромных объёмах реальной речи. Сеть, по сути, выучила, как звучит человеческая речь, поэтому, получив звук перед пробелом, она генерирует продолжение, соответствующее естественной статистике речи – правильному контуру высоты тона, правильному способу перехода гласного в согласный – намного убедительнее, чем фиксированное правило.

Первым широко развёрнутым примером стал WaveNetEQ от Google, появившийся в Google Duo в 2020 году. Это генеративная модель на основе WaveRNN от DeepMind, которая создаёт фрагменты звука для заполнения пропусков. Поскольку в Duo используется сквозное шифрование, модель работает полностью на устройстве получателя, и Google спроектировал её достаточно быстрой для этого при сохранении высокого качества звука. Её обучали на речи более 100 дикторов на 48 языках, смешанной с разнообразным фоновым шумом, чтобы она корректно работала в шумных помещениях (Google Research, «Improving Audio Quality in Duo with WaveNetEQ», апрель 2020). Её честное заявленное ограничение показательно: WaveNetEQ «учится правдоподобно продолжать речь на короткой дистанции» – он может завершить слог, но не предсказывает слова (Google Research, апрель 2020). Нейросетевой PLC, как и любой PLC до него, маскирует помехи; мысли он не читает.

Самое важное недавнее событие – выпуск Opus 1.5 в марте 2024 года. В нём появился PLC на основе глубокого обучения, который вместо ручных эвристик позволяет нейросети восстанавливать пропавший звук. Его авторы заняли второе место в конкурсе Microsoft Audio Deep Packet Loss Concealment Challenge 2022 года, используя ту же базовую технологию (Xiph.Org, «Opus 1.5 Released», март 2024). Важно, что deep PLC – это чисто декодерное обновление: оно не влияет на передачу данных, поэтому полностью совместимо со стандартом Opus (RFC 6716), и любой кодер Opus может работать с декодером, в котором включён deep PLC. Включить его можно на этапе сборки с помощью флага --enable-deep-plc (увеличение размера бинарника примерно на 1 МБ) и во время выполнения – установив сложность декодера на 5 или выше; дополнительная нагрузка при высоких потерях составляет около 1 процента одного ядра CPU ноутбука (Xiph.Org, март 2024).

Нейросетевой вокодер, делающий это достаточно дешёвым для использования в продакшене, сам по себе заслуживает внимания: Opus 1.5 представил FARGAN (framewise autoregressive generative adversarial network) со сложностью около 600 MFLOPS – в пять раз меньше, чем у заменённого им вокодера LPCNet, – и позволяет работать менее чем на 1 проценте ядра CPU современного смартфона (Xiph.Org, март 2024).

Когда потеря слишком велика, чтобы её вообразить: DRED

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

В Opus 1.5 появился яркий ответ – Deep REDundancy (DRED). Обычные кодеки ограничивают длину пакетов – обычно до 20 миллисекунд – ради низкой задержки, что ограничивает объём доступной истории. DRED снимает это ограничение для избыточной копии: он использует нейросетевой компрессор (вариационный автоэнкодер с оптимизацией по скорости и искажению), чтобы упаковать до одной полной секунды недавнего звука примерно в 12–32 килобита в секунду накладных расходов, помещаемых в padding обычного пакета Opus – так что старые декодеры просто игнорируют этот дополнительный объём. В результате каждый 20-миллисекундный кадр фактически передаётся около 50 раз, распределённый по многим последующим пакетам, при стоимости, близкой к традиционному механизму избыточности (Xiph.Org, март 2024). В тестах проекта на полном стеке WebRTC DRED сохранял разборчивость речи даже при 90 процентах потерь пакетов (Xiph.Org, март 2024). DRED пока не является финальным стандартом – он разрабатывается в рабочей группе IETF mlcodec, – поэтому версию из Opus 1.5 стоит считать экспериментальной. Однако направление ясно: именно на границе между «маскировкой» и «избыточностью» формируется будущее устойчивого звука.

Что на самом деле делает WebRTC: Expand и Merge в NetEQ

В WebRTC маскировка потерь пакетов – это не отдельный модуль, который можно подключить: она встроена в NetEQ, тот же адаптивный буфер джиттера, что сглаживает неравномерный приём пакетов. Мы подробно разбираем его в статье про буфер джиттера NetEQ. Каждые 10 миллисекунд аудиоустройство вызывает функцию NetEQ GetAudio, и NetEQ обязан вернуть фрагмент звука. Когда нужный пакет отсутствует – либо потерян, либо просто опоздал – декодировать нечего, и вместо тишины NetEQ выполняет операцию, которую документация WebRTC называет Expand: он генерирует маскировку потерь пакетов, «экстраполируя оставшийся звук из sync buffer или поручая его создание декодеру» (документация WebRTC NetEq, chromium.googlesource.com).

Эта одна фраза охватывает двухуровневую структуру. У NetEQ есть встроенная маскировка замещением формы волны – описанная выше экстраполяция по периоду основного тона, – которая работает с любым кодеком. Однако для кодеков с собственной, более эффективной маскировкой NetEQ вместо этого просит декодер кодека восстановить пропущенный звук. В случае Opus это означает, что NetEQ может задействовать собственную модельную (а в Opus 1.5 – нейросетевую) маскировку Opus, поэтому стек WebRTC на актуальной версии Opus при потерях пакетов звучит заметно лучше, чем при использовании только общего пути.

Когда реальный пакет наконец приходит после того, как Expand синтезировал звук, просто состыковать их нельзя – на стыке возникнет щелчок, как и на границах пробела при вставке нулей. Поэтому NetEQ выполняет вторую операцию – Merge, которая плавно соединяет выход маскировки со свежедекодированным реальным звуком, чтобы стык стал неслышным (документация WebRTC NetEq). Expand заполняет дыру; Merge скрывает края заплатки. Вместе они составляют полную постфактумную обработку потерь в WebRTC.

Рисунок 2. Как NetEQ маскирует потерянный пакет: Expand синтезирует звук в пробел; Merge кроссфейдит реальный пакет обратно, когда он возвращается.

Разбор на числах: как быстро падает качество в зависимости от паттерна

Числа наглядно показывают проблему с сериями. Допустим, звук использует кадры длительностью 20 миллисекунд – тогда каждый потерянный пакет оставляет пробел в 20 миллисекунд. Рассмотрим две сети, в которых теряется по 5 процентов пакетов, но с разным паттерном.

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

размер кадра          = 20 мс
изолированная потеря  = 1 кадр
маскируемый интервал  = 1 × 20 мс = 20 мс   ← в пределах «неслышно»

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

размер кадра          = 20 мс
серийная потеря       = 6 кадров
маскируемый интервал  = 6 × 20 мс = 120 мс  ← далеко за пределами естественного зацикливания

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

PLC – один из трёх инструментов; знайте, какую работу он выполняет

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

Ретрансляция (RTX) просит отправителя повторно отправить потерянный пакет. Она восстанавливает точный звук – без догадок – но требует как минимум одного сетевого round trip, поэтому эффективна только тогда, когда задержка round trip мала по сравнению со временем удержания буфера джиттера. На канале с высокой задержкой ретранслированный пакет приходит слишком поздно для воспроизведения (определено в IETF RFC 4588).

Упреждающая коррекция ошибок (FEC), включая встроенную низкоскоростную избыточность Opus и формат RED, заранее передаёт избыточные данные, чтобы потерянный пакет можно было восстановить без повторного запроса. Она расходует полосу пропускания на каждом пакете – независимо от того, были потери или нет – но не увеличивает задержку round trip. Подробно о её компромиссах мы рассказываем в статье про упреждающую коррекцию ошибок, in-band FEC и избыточность RED.

Маскировка потерь пакетов (PLC) не делает ни того, ни другого – она синтезирует замену на стороне получателя с нулевой добавленной задержкой и нулевой дополнительной полосой пропускания, но это догадка, и она может лишь продолжить то, что уже было услышано. Эти три инструмента дополняют друг друга, а не заменяют: хорошо построенный стек WebRTC использует FEC, чтобы избежать большинства потерь, RTX – чтобы восстановить часть оставшихся, когда позволяет задержка, и PLC – чтобы замаскировать то, что всё же прошло. DRED интересен именно тем, что стирает границу – это избыточность, доставленная в путь маскировки.

ИнструментЧто делаетЦена в задержкеЦена в полосеВозвращает точный звук?
Ретрансляция (RTX)Пересылает потерянный пакет по запросуОдин round tripТолько при потереДа
Коррекция ошибок (FEC/RED)Заранее шлёт избыточные данныеНетНа каждом пакетеДа (в пределах избыточности)
Маскировка (PLC)Синтезирует замену у получателяНетНетНет – это догадка

Частая ошибка: считать высокий уровень маскировки багом в PLC

Самая дорогая ошибка команд – увидеть высокий уровень маскировки и сделать вывод: «Наш PLC сломан». PLC находится в самом конце конвейера; сильный шум маскировки почти всегда означает, что пакеты просто не доходят – реальные сетевые потери, сбой управления перегрузкой или отправитель, снизивший битрейт, – а не то, что алгоритм маскировки выбрал плохие параметры. Замена на более продвинутый PLC, когда настоящая проблема – 8 процентов потерь в серии, делает «булёж» чуть приятнее, но дыры не закрывает. Сначала измерьте потери; почините сеть или добавьте избыточность; настройте маскировку – в последнюю очередь.

Вторая ловушка – судить о PLC по неверной метрике. Устоявшаяся практика – оценивать качество по проценту потерь и общей оценке звука, но ни один из этих показателей не отражает, как на самом деле работает маскировка: один и тот же процент потерь может звучать совершенно по-разному в зависимости от серийности (как показал численный анализ). В настоящее время в отрасли применяются специальные метрики – например, PLCMOS от Microsoft, разработанная специально для оценки качества маскировки и представленная вместе с Deep PLC Challenge 2022 года именно потому, что общие аудиометрики, такие как PESQ, не замечали артефакты, связанные с маскировкой (Xiph.Org, март 2024). Если вы сравниваете две реализации PLC, делайте это на реалистичных трассах с серийными потерями и с использованием метрики, учитывающей маскировку, а не на равномерных случайных потерях с PESQ.

Третья ловушка – считать, что нейросетевой PLC бесплатен. Он недорог – примерно 1 процент ядра CPU для deep PLC в Opus, – но «дёшево» не значит «бесплатно». Он требует специальной сборки, в которую он скомпилирован, и достаточно высокого уровня сложности декодера, чтобы его активировать (5 или выше для deep PLC) (Xiph.Org, март 2024). Команда, ожидающая нейросетевой маскировки, но использующая стандартную сборку декодера, получает общий путь и удивляется, почему звук всё ещё «булькает».

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

Фора Софт с 2005 года внедряет звук в реальном времени в видеоконференции, телемедицину, онлайн-обучение и лайв-шопинг, а анализ поведения маскировки – рутинная часть диагностики и настройки этих систем. Когда клиент жалуется на роботизированный или «булькающий» звук, первый шаг – посмотреть счётчики маскировки в getStats, определить, являются ли потери изолированными или серийными, и решить, где устранять проблему: выше по цепочке (сеть, управление перегрузкой, добавление FEC) или в настройках декодера (включение более эффективного пути маскировки Opus). Телемедицинский звонок через больничный Wi-Fi и вебинар на 500 человек с разнородными каналами пользователей теряют пакеты по-разному, и правильный набор средств устойчивости – FEC, ретрансляция, маскировка, а при необходимости – нейросетевая избыточность – выбирается на основе измеренного профиля потерь, а не догадок. Мы интегрируем эти метрики с самого начала, чтобы фраза «звук звучит сломанным» превращалась в конкретное число, которое можно измерить и проанализировать.

Главное

  • PLC восстанавливает потерянный пакет синтезированным звуком, чтобы слушатель не слышал «провала».
  • Четыре подхода: вставка нулей (вызывающая щелчки), замена формой волны, модельный и нейросетевой.
  • WebRTC использует операцию Expand в NetEQ для маскировки; Merge возвращает реальный пакет в поток.
  • Нейросетевой PLC (WaveNetEQ, deep PLC в Opus 1.5) звучит значительно лучше и потребляет около 1% ядра CPU.
  • PLC продолжает предыдущую последовательность; восстановить потерянные слова может только избыточность, например DRED.
  • Процент потерь не отражает серийность; именно серийные потери вызывают роботизированный эффект в звуке.

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

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

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