Содержание статьи +
- Кратко
- Зачем это важно
- Проблема: пакет нельзя запросить повторно
- Что значит «forward»: платить вперёд
- Три формы, которые может принимать FEC
- Opus in-band FEC: дешёвый способ восстановления последнего кадра
- RED: оборачиваем целые пакеты ради надёжной защиты
- Фронтир: Deep REDundancy (DRED)
- Разобранный пример: помогает ли FEC или просто стоит
- Частая ошибка: оставить FEC на максимальной мощности в чистой сети
- Где здесь Фора Софт
- Главное
- Что почитать дальше
Кратко
Forward error correction, или FEC, – это метод, при котором дополнительно передаётся небольшая часть данных заранее, чтобы при потере пакета приёмник мог восстановить его без повторного запроса: без задержек и без обратного обмена по сети. В реальном времени применяются два механизма FEC: встроенный in-band FEC кодека Opus, который помещает копию предыдущего кадра низкого качества в следующий пакет, и более старый формат RED из 1997 года (RFC 2198), оборачивающий целые предыдущие пакеты в текущий для более надёжной защиты от потери нескольких кадров. Компромисс всегда один: FEC расходует полосу пропускания на каждом пакете независимо от того, произошла ли потеря, – поэтому он полезен на «плохих» каналах, но незаметно тратит битрейт на стабильных, а если потеря вызвана перегрузкой сети, добавление FEC только усугубит ситуацию. Эта статья простым языком объясняет, почему повторная передача не подходит для живого звука, как именно работают Opus in-band FEC и RED на проводе, во сколько обходится каждый из них и по какому правилу решать, включать ли их.
Зачем это важно
Если вы разрабатываете видеозвонки, телемедицину, контакт-центры или онлайн-обучение, именно потеря пакетов определяет, будет ли звук чистым или превратится в роботизированное заикание – а FEC и является главным инструментом, позволяющим исправить это до того, как пользователь услышит обрыв. Выбор между Opus in-band FEC и RED, а также битрейт, который вы готовы выделить на каждый из них, – это не техническая деталь, которую можно оставить на настройках по умолчанию, а важное продуктовое решение с реальной стоимостью и ощутимыми последствиями для качества.
Статья предназначена для продукт-менеджеров, основателей и операционных руководителей, которым важно понять, что даёт FEC, сколько он стоит и в каких случаях может навредить, – чтобы правильно интерпретировать жалобы на качество и чётко сформулировать задачу инженерам. Старший инженер также найдёт здесь каждый факт с ссылкой на соответствующие RFC, спецификацию Opus и инженерную историю WebRTC.
Проблема: пакет нельзя запросить повторно
Начнём с ограничения, которое исключает очевидное решение. Когда пакет теряется в сети, проще всего попросить отправителя отправить его снова. Этот приём называется повторной передачей, сокращённо RTX, и для RTP-медиа он описан в IETF RFC 4588 (июль 2006). При скачивании файла или загрузке веб-страницы повторная передача – именно то, что нужно: вы немного ждёте, недостающий кусок приходит, и ничего не теряется.
Для живого разговора повторная передача обычно приходит слишком поздно. Вот почему – с цифрами на слух. Запрос пакета заново требует одного сетевого кругового обхода (round trip) – это время, пока ваш запрос дойдёт до отправителя, плюс время, пока пересланный пакет вернётся:
время кругового обхода (RTT) = до отправителя + обратно
пример трансконтинентального RTT = 150 ms
время удержания jitter buffer = 60 ms
вердикт: 150 ms > 60 ms → пересланный пакет приходит после своего слотаПриёмник не может поставить разговор на паузу и ждать. В WebRTC аудиоустройство требует свежий фрагмент звука – ровно 10 миллисекунд – точно вовремя, 100 раз в секунду, бесконечно (этот «такт» мы разбираем в статье про аудиоконвейер WebRTC). Если пересланный пакет не поступит до момента воспроизведения, он становится бесполезным. Повторная передача помогает только тогда, когда задержка кругового обхода короче, чем время, которое буфер дрожания приёмника может удерживать звук – об этом подробно в статье про буфер дрожания NetEQ. На канале с большой задержкой – мобильном или трансконтинентальном – это условие часто не выполняется.
Значит, нужен способ восстановить потерянный пакет без кругового обхода. Именно это и называется forward error correction.
Что значит «forward»: платить вперёд
Forward error correction – это семейство методов, при которых избыточная информация передаётся заранее, добавляясь в обычный поток пакетов, чтобы потерянный пакет можно было восстановить на приёмной стороне сразу после обнаружения его потери – без запроса к отправителю и без дополнительной задержки. Слово «forward» (вперёд) здесь ключевое: вы платите заранее, на каждом пакете, ещё до того, как узнаете, будет ли потерян именно этот пакет. RFC 8854, спецификация FEC для WebRTC (январь 2021), формулирует цель прямо: FEC – это «отправка избыточной информации в исходящем потоке пакетов, чтобы информацию можно было восстановить даже при потере пакета».
Представьте, что вы отправляете кому-то важный документ и кладёте его копию в тот же конверт, а ещё одну – в завтрашний. Если один конверт потеряется на почте, у получателя останется копия из другого. Вы тратите лишнюю бумагу каждый день, хотя большинство конвертов доходят без проблем. Эта трата – в случае сети это полоса пропускания – и есть цена за то, чтобы не писать в ответ и не просить переслать.
Эта модель «плати вперёд» – самое важное, что нужно понимать о FEC, потому что именно она является его главным недостатком. RFC 8854 прямо указывает на цену: «использование FEC всегда приводит к передаче избыточных данных, и общий объём данных должен оставаться в пределах ограничений полосы… это приведёт к тому, что для основного кодирования останется меньше полосы, даже когда избыточные данные не используются». Иными словами, в чистой сети FEC – это чисто накладные расходы, которые могут даже снизить качество звука, поскольку битрейт, который он потребляет, отнимается у основного аудиопотока.
Три формы, которые может принимать FEC
RFC 8854 делит способы применения FEC для RTP-медиа на три механизма, и различия между ними существенны, поскольку у каждого – совершенно разные накладные расходы. Понимание этих трёх подходов достаточно, чтобы уверенно участвовать в любом техническом разговоре об устойчивости аудиопотока с инженером.
Первый – отдельный поток FEC (RFC 5956). В этом случае избыточность передаётся как самостоятельный RTP-поток со своими заголовками. Один ремонтный пакет может защитить сразу несколько медиапакетов – это эффективно для видео. Однако для звука, где обычно на кадр приходится один небольшой пакет, каждый FEC-пакет несёт полный набор заголовков IP, UDP и RTP, а накладные расходы оказываются слишком велики по сравнению с крошечной аудионагрузкой. RFC 8854 прямо указывает: отдельный поток FEC для звука «несёт более высокие накладные расходы, чем другие механизмы, и поэтому НЕ РЕКОМЕНДУЕТСЯ».
Второй – избыточное кодирование (RFC 2198, формат RED), при котором избыточные данные встраиваются в тот же пакет, что и основной аудиопоток, и используют один и тот же набор заголовков. Такой подход эффективен именно потому, что пакет имеет единый заголовок, что является естественной формой для передачи звука. Формат RED мы рассмотрим подробнее ниже.
Третий – codec-specific in-band FEC, при котором сам кодек встраивает избыточность прямо в свой битстрим. Так работает Opus, а также речевой кодек AMR, используемый в мобильной телефонии (о нём – в статье про речевые кодеки). Поскольку избыточность находится внутри полезной нагрузки кодека и «не требует дополнительного обрамления», RFC 8854 отмечает, что такой подход «может быть немного эффективнее», чем RED. Именно этот механизм используется по умолчанию в большинстве звонков WebRTC.
| Механизм FEC | Где живёт избыточность | Накладные расходы заголовков | Хорош для | RFC |
|---|---|---|---|---|
| Отдельный поток FEC | Свой RTP-поток | Высокие (свои заголовки) | Видео, многопакетные кадры | 5956 / 8627 |
| Избыточное кодирование (RED) | Внутри того же пакета | Низкие (общий заголовок) | Звук, защита нескольких кадров | 2198 |
| In-band FEC кодека | Внутри нагрузки кодека | Минимальные (без обрамления) | Звук, защита одного кадра | 6716 (Opus) |
Opus in-band FEC: дешёвый способ восстановления последнего кадра
Opus – это кодек, который WebRTC использует почти для всей речи (мы подробно разбираем его в статье про кодек Opus). Его встроенный FEC работает по механизму, который спецификация Opus, RFC 6716 (сентябрь 2012), называет LBRR – Low Bit-Rate Redundancy (RFC 6716, §4.2.5). Идея проста: когда FEC включён, кодер берёт только что отправленный кадр, перекодирует его с гораздо меньшим битрейтом и вкладывает эту уменьшенную копию в следующий пакет. RFC 8854 описывает это чётко: для Opus «кадры, признанные важными, перекодируются с меньшим битрейтом и добавляются к следующей нагрузке, позволяя частично восстановить потерянный пакет».
Так что пакет N содержит две вещи: полную копию кадра N и упрощённую копию кадра N−1. Если пакет N−1 потерян, но пакет N пришёл, декодер извлекает резервную копию кадра N−1 из пакета N и воспроизводит её – с ухудшенным качеством, но гораздо лучше, чем при попытке угадать потерянный фрагмент. В этом и состоит разница между FEC и сокрытием потерь пакетов: сокрытие придумывает правдоподобную замену потерянному звуку, а FEC восстанавливает то, что было на самом деле, поскольку была отправлена реальная копия. Эти инструменты мы сравниваем бок о бок в статье про сокрытие потери пакетов (PLC).
У Opus in-band FEC есть одно жёсткое ограничение, определяющее, как его можно использовать. RFC 8854 прямо указывает: «этот механизм может передавать информацию избыточности только для непосредственно предшествующего аудиокадра; следовательно, декодер не способен полностью восстановить несколько подряд потерянных пакетов, что может стать проблемой в беспроводных сетях». Проще говоря: Opus FEC защищает только от потери одного пакета за раз. Если потеряны два пакета подряд – пачку (burst), как это часто бывает при плохом Wi-Fi или сотовой связи, – то резервная копия первого потерянного кадра находилась внутри второго потерянного пакета, и она тоже пропала.
Как включить и сколько битрейта он потребляет
Чтобы принимать Opus FEC, приёмник сообщает о своей готовности с помощью параметра useinbandfec=1, определённого в формате RTP-нагрузки Opus (RFC 7587, июнь 2015). На стороне отправителя кодеру необходимо указать, что FEC требуется, и задать ожидаемый уровень потерь, чтобы он мог определить степень защиты. Два практических аспекта из инженерной истории WebRTC определяют, будет ли кодер вообще что-либо делать.
Во-первых, Opus начинает производить LBRR только когда считает, что потеря происходит. Команда WebRTC в Mozilla, включая аудио-FEC в Firefox, обнаружила, что «порог включения LBRR внутри Opus – около 1%» потери пакетов (Mozilla, «Audio FEC Experimentation», ноябрь 2016). Ниже этого FEC ничего не отправляет – а значит, он не может защитить вас от первой пачки внезапной потери, только от продолжающейся.
Во-вторых, избыточность – это не дополнительный битрейт, а часть вашего существующего бюджета битрейта, которую приходится выделять на неё. Реализация WebRTC вычитает битрейт FEC из целевого значения, и объём защиты ограничен: инженерный анализ стека WebRTC показал, что коррекция Opus FEC «ограничена 25%» и что «битрейт для FEC вычитается из целевого максимального битрейта» (webrtcHacks, «RED: Improving Audio Quality with Redundancy», август 2020). Кроме того, Opus требует достаточного общего битрейта, чтобы вообще могло появиться место для дублирования данных. Mozilla выяснила, что при стандартных 40 кбит/с в Firefox двухканальный FEC не активируется – битрейт «пришлось бы поднять до 58000» бит в секунду (58 кбит/с), прежде чем стерео-Opus начнёт генерировать пакеты LBRR (Mozilla, ноябрь 2016). Вывод: чтобы Opus FEC работал, нужно увеличить аудиобитрейт, чтобы хватило места для избыточности – иначе он просто ничего не делает, оставаясь незамеченным.
RED: оборачиваем целые пакеты ради надёжной защиты
Когда одного кадра защиты недостаточно, более старый и гибкий подход – RED – RTP Payload for Redundant Audio Data, определённый в RFC 2198 ещё в сентябре 1997 года. RED изначально разрабатывался для аудиоконференц-систем раннего интернета, и его идея хорошо выдержала испытание временем: взять одну или несколько целых предыдущих аудионагрузок и упаковать их в текущий RTP-пакет – всё под одним RTP-заголовком, с небольшим дополнительным заголовком.
RFC 2198 чётко описывает формат. RED-пакет представляет собой контейнер: сначала идёт обычный RTP-заголовок (поля которого описывают основной, самый свежий аудиопоток), затем – короткие заголовки для каждой избыточной нагрузки, после чего следуют сами данные. Заголовок каждого избыточного блока занимает четыре байта и содержит флаг (F), указывающий, следует ли ещё один блок, 7-битный тип нагрузки, определяющий кодек, 14-битное смещение метки времени (timestamp offset), показывающее, насколько ранее по времени находится звук этого блока, и 10-битное значение длины блока. Заголовок основного блока состоит всего из одного байта, поскольку его метка времени и длина уже известны из RTP-заголовка и общего размера пакета. Спецификация жёсткая: «избыточное кодирование НЕ ДОЛЖНО превышать битрейт основного потока» – резервные копии должны быть компактными.
В WebRTC RED указывается в описании сессии как кодек с именем red, и для его использования его размещают перед Opus в списке кодеков. Согласованная строка выглядит как a=rtpmap:111 red/48000/2 – RED на 48 кГц, 2 канала – и приложение перестраивает кодеки так, чтобы RED оборачивал нагрузку Opus (webrtcHacks, август 2020). Количество избыточных копий, передаваемых с помощью RED, называется дистанцией (distance): при дистанции 1 каждый пакет содержит также предыдущий, при дистанции 2 – два предыдущих.
Дистанция 2 – золотая середина, и она примерно удваивает битрейт
Самый полезный производственный результат по RED дал возврат WebRTC благодаря инженерной работе. При жёсткой 60%-ной случайной потере пакетов команда оценивала качество звука по доле сэмплов, которые приёмнику пришлось скрыть – отношению concealedSamples к totalSamplesReceived из статистического API WebRTC. Числа говорят сами за себя:
потеря в сети = 60%
без RED: скрыто 60% сэмплов → слова трудно разобрать
RED, дистанция 1: скрыто 32% → слышны артефакты
RED, дистанция 2: скрыто 18% → почти идеальный звукДистанция 2 снизила уровень сокрытия с 60% до 18% при 60%-ной потере пакетов, и «дистанция 2… – это объём избыточности, который теперь используется» в WebRTC (webrtcHacks, август 2020). Платой за это становится полоса пропускания: RED с одной избыточной копией примерно удваивает аудиобитрейт – с около 30 кбит/с до около 60 кбит/с в опубликованном тесте, «плюс дополнительные 10 кбит/с на заголовки», – а дистанция 2 поднимает его ещё выше (webrtcHacks, август 2020). Две техники помогают сдерживать эту нагрузку. Voice activity detection (детектор речевой активности) означает, что избыточные копии отправляются только тогда, когда кто-то действительно говорит, а discontinuous transmission (DTX) останавливает передачу во время пауз – оба механизма подробно описаны в статье про VAD и DTX – так что удвоенный битрейт применяется лишь к тем фрагментам звонка, где присутствует речь.
Причина, по которой дистанция 2 важнее дистанции 1, – снова проблема пачки: если теряются два пакета подряд, единственный резерв при дистанции 1 может сам оказаться в потерянном пакете, тогда как дистанция 2 распределяет два резерва по двум последующим пакетам и потому выдерживает пачку из двух пакетов. Как сказал инженер WebRTC, проделавший эту работу, «человеческий мозг способен переносить определённый уровень прерывистой тишины» – дистанция 2 держит пропуски достаточно короткими, чтобы оставаться ниже этого порога.
Фронтир: Deep REDundancy (DRED)
И Opus in-band FEC, и RED сталкиваются с одной и той же проблемой при длинных сериях потерь. Opus FEC восстанавливает только один кадр; RED на практике – два-три. В сети, где теряется сразу десять пакетов подряд – то есть четверть секунды речи – ни один из этих методов не поможет, потому что они просто не хранят достаточно информации без неприемлемого роста битрейта.
Предлагаемый ответ – DRED – Deep Audio Redundancy – нейросетевое расширение Opus, которое стандартизируется рабочей группой IETF mlcodec (draft-ietf-mlcodec-opus-dred-05, срок действия – июль 2026; авторы – специалисты из Google и Meta). DRED заменяет традиционную идею «перекодировать последний кадр в более низком качестве» на нейросетевой компрессор – вариационный автоэнкодер, оптимизированный по критерию «скорость–искажение», – способный сжимать значительно более длинный фрагмент недавнего звука в очень небольшой объём данных. В черновике прямо указано: если существующий механизм LBRR в Opus «ограничен одной кадровой избыточностью и обычно использует около 2/3 битрейта обычного пакета Opus», то DRED «позволяет включать до одной секунды или более избыточности в каждый пакет, используя битрейт примерно в 1/50 от обычного битрейта Opus» (draft-ietf-mlcodec-opus-dred-05, §2). Избыточность передаётся внутри поля заполнения (padding) пакета Opus, поэтому старые декодеры, не поддерживающие DRED, просто игнорируют её.
Практический эффект заключается в том, что каждый кадр фактически передаётся несколько раз в последующих пакетах, так что даже длинную паузу можно заполнить звуком, который был произнесён на самом деле, а не восстановлен. DRED появился в экспериментальной форме в Opus 1.5 (март 2024) – в том же релизе, что и нейросетевое сокрытие потерь пакетов. Поскольку это всё ещё черновик, точные цифры следует считать предварительными – но направление ясно: граница между «избыточностью» и «восстановлением» размывается, и следующее поколение устойчивого звука будет существовать на этой грани.
Разобранный пример: помогает ли FEC или просто стоит
Решение о включении FEC сводится к оценке: оправдывает ли улучшенное качество звука дополнительные затраты на полосу пропускания. Рассмотрим конкретный пример. Допустим, звонок один на один использует кодек Opus со скоростью 40 кбит/с, и вы решаете, стоит ли добавлять RED с параметром дистанции 1 – это примерно удваивает аудиобитрейт.
основной аудиобитрейт = 40 kbps
RED дистанция 1 добавляет ≈ 40 kbps + ~10 kbps заголовок
итого аудиобитрейт с RED ≈ 90 kbpsТеперь сравним эти 50 кбит/с лишних затрат со следующей ситуацией:
На чистом офисном соединении с почти нулевой потерей эти 50 кбит/с не дают никакого выигрыша – пакетов, которые нужно восстанавливать, просто нет, – и только конкурируют за полосу пропускания с другими данными. Согласно рекомендации RFC 8854, следует предпочесть повторную передачу, если круговой обход укладывается в допустимый бюджет задержки, и «в противном случае ДОЛЖЕН передавать только тот объём FEC, который необходим для защиты от наблюдаемой потери пакетов». Таким образом, на канале с минимальными потерями правильным решением будет использовать минимальное количество FEC или вовсе отказаться от него.
На «плохом» мобильном соединении с 8% потерей пакетов те же 50 кбит/с – это разница между чистым звонком и роботизированным голосом, а на фоне видеопотока, способного передавать до 1,5 Мбит/с, дополнительные 50 кбит/с на защиту звука – это погрешность округления. Как отметил анализ webrtcHacks, стоимость полосы RED «по сравнению с более чем мегабитом в секунду видеоданных… не кажется такой уж большой».
И есть случай, когда FEC активно вредит: когда потеря вызвана перегрузкой – канал и так заполнен. Добавление избыточных данных отправляет больше данных в насыщенную сеть, увеличивая потерю. RFC 8854 предупреждает, что «если потеря вызвана перегрузкой сети, дополнительная полоса, используемая избыточными данными, может фактически ухудшить ситуацию». RFC 2198 делает то же замечание про RED: слишком много избыточности «увеличит перегрузку сети и, следовательно, потерю пакетов, ухудшая ту самую проблему, для решения которой избыточность и применялась». Вот почему производственные стеки включают и отключают FEC в зависимости от измеренной потери, а не оставляют его постоянно включённым.
Частая ошибка: оставить FEC на максимальной мощности в чистой сети
Самая частая ошибка – воспринимать FEC как бесплатный апгрейд качества и включать его везде. Он не бесплатен: каждая дополнительная копия – это битрейт, отведённый от основного потока или добавленный к каналу, и на стабильном соединении это чистая трата, которая может снизить качество, а не повысить. Хуже того, если наблюдаемые потери вызваны перегрузкой, FEC на максимальной мощности только усугубляет проблему. Решение – адаптивный FEC: отслеживать реальную долю потерь по отчётам приёмника RTCP и отправлять столько избыточности, сколько оправдана наблюдаемая потеря, как рекомендует RFC 8854. «Поставил и забыл» – здесь не работает.
Вторая ловушка – путать FEC с повторной передачей и использовать неподходящий инструмент. Эти подходы не взаимозаменяемы. RTX восстанавливает точный пакет, но требует кругового обхода, поэтому он оправдан при низкой задержке; FEC использует постоянную полосу пропускания, но не добавляет задержек, поэтому он предпочтителен при высокой задержке. Правило из RFC 8854 чётко сформулировано: выбирать RTX или восстановление по принципу повторной передачи, «когда RTT соединения укладывается в бюджет задержки приложения», и использовать FEC во всех остальных случаях. Применение FEC в низколатентной локальной сети – пустая трата полосы пропускания; использование RTX на высокозадерживающем мобильном канале приводит к восстановлению пакетов слишком поздно для их своевременного воспроизведения.
Третья ловушка – ждать, что Opus in-band FEC справится с пакетными потерями. Поскольку он защищает только один предыдущий кадр, он отлично работает при изолированных случайных потерях, но терпит крах при последовательных потерях – именно такие характерны для реальных беспроводных сетей. Если ваш трафик идёт пачками, то RED с радиусом 2 – или со временем DRED – будет более подходящим решением под ваш паттерн потерь; увеличение уровня FEC в Opus не поможет при пропаже нескольких пакетов подряд.
Где здесь Фора Софт
Фора Софт интегрирует звук в реальном времени в продукты для видеоконференций, телемедицины, онлайн-обучения и live-шопинга с 2005 года, и выбор подходящего набора средств защиты от потерь пакетов – рутинная часть настройки этих систем. Когда клиент сообщает о роботизированном или пропадающем звуке, мы анализируем долю и характер потерь пакетов по статистике WebRTC, а затем принимаем осознанное решение: оставить Opus in-band FEC при случайных потерях, переключиться на RED с дистанцией 2 при пакетных потерях, увеличить аудиобитрейт, чтобы обеспечить пространство для избыточности, и поддерживать всё это адаптивным, чтобы стабильное соединение не страдало от избыточных мер. Телемедицинский звонок в больничной сети Wi-Fi и масштабный вебинар на разнородных потребительских каналах теряют пакеты по-разному, и правильный ответ определяется на основе измеренного профиля потерь, а не задаётся по умолчанию. Мы собираем эту статистику с самого начала, чтобы проблема «звук разваливается» превращалась в конкретное число, по которому можно принимать решения.
Главное
- FEC передаёт избыточность заранее, восстанавливая потерянные данные без обратного запроса, в отличие от RTX.
- FEC использует полосу пропускания на каждом пакете: полезен на «плохих» каналах, но тратит битрейт на стабильных.
- Opus in-band FEC (LBRR) дешево дублирует предыдущий кадр, но защищает только одну изолированную потерю.
- RED (RFC 2198) оборачивает целые пакеты; при дистанции 2 эффективность сокрытия упала с 60% до 18% при 60% потерь.
- При перегрузке сети FEC может усугубить ситуацию – используйте его адаптивно, основываясь на измеренной потере.
- DRED упаковывает около секунды избыточности при примерно 1/50 от битрейта – перспективное решение для длинных пакетов.