Содержание статьи +
- Кратко
- Почему это важно
- Что на самом деле значит «конвейер»
- Сторона отправки: захват и очистка
- Сторона отправки: кодирование и упаковка
- Сеть: часть, которую вы не контролируете
- Сторона приёма: NetEQ – мозг аудио WebRTC
- Бюджет задержки, который можно повесить на стену
- Где здесь Фора Софт
- Главное
- Что почитать дальше
Опубликовано: 2026-06-06 · Время чтения: 24 мин · Автор: Николай Сапунов, CEO Фора Софт
Кратко
Аудиоконвейер WebRTC – это последовательность шагов, передающих голос в реальном времени от микрофона одного человека к уху другого: захват, очистка, кодирование, упаковка в пакеты, отправка по сети, буферизация, декодирование и воспроизведение. Большинство этих этапов призваны бороться с тремя основными проблемами – эхом, шумом и нестабильностью сети, – и каждый из них занимает несколько миллисекунд. Поэтому у всей цепочки есть бюджет задержки, который качественные продукты стараются держать примерно на уровне ниже 200 мс «ото рта до уха». Две ключевые составляющие, определяющие, звучит ли звонок профессионально или любительски, – это обработка сигнала на стороне захвата (эхоподавление, шумоподавление, управление уровнем усиления) и буфер джиттера на стороне приёма (NetEQ); обе они остаются незаметными, пока не начнут работать с ошибками. В этой статье мы подробно рассмотрим каждый этап конвейера простым языком, назовём реальные компоненты, используемые в браузерах и нативных стеках, и представим бюджет задержки, который можно повесить на стену.
Почему это важно
Если вы строите приложение для телемедицины, инструмент видеоконференций, контакт-центр, стрим для лайв-шопинга или онлайн-класс, аудиоконвейер – это та часть, по которой пользователи судят вас в первую очередь. Замёрзший кадр видео людям простят; эхо, роботизированные провалы или голос, который то появляется, то пропадает, – повесят трубку. Эта статья для менеджера продукта, основателя или операционного руководителя, которому нужно понять, что происходит с голосом между двумя телефонами, достаточно хорошо, чтобы принимать решения, читать баг-репорт инженера и знать, про какую ручку спросить, когда клиент говорит «звук сломан». Опытный инженер тоже найдёт каждое утверждение со ссылкой на документацию WebRTC или соответствующий RFC. К концу вы сможете нарисовать конвейер сами и объяснить, зачем нужен каждый блок.
Что на самом деле значит «конвейер»
Перед диаграммой важно чётко усвоить одну идею. Система аудио в реальном времени – это не одна программа, а эстафета. Крошечный фрагмент звука – обычно 10–20 миллисекунд, его называют фреймом, – передаётся от одного «бегуна» к следующему, и звук доходит до слушателя целым только в том случае, если каждый этап пройден правильно и быстро. Слово «конвейер» как раз отражает эту суть: звук входит с одного конца как непрерывная физическая волна, разбивается на фреймы, каждый из которых проходит через цепочку обрабатывающих стадий, и в итоге непрерывная волна выходит с другого конца – в чьё-то ухо.
Технология, благодаря которой это работает в браузере – без плагинов и установки, – называется WebRTC (Web Real-Time Communication): это набор стандартов и общий код, который используют все крупные браузеры. Когда вы заходите на видеозвонок в Chrome, Safari или Firefox, аудиоданные проходят через конвейер, описанный ниже. Нативные мобильные приложения обычно используют тот же открытый код (libwebrtc) «под капотом», поэтому конвейер почти идентичен – как во вкладке браузера, так и в приложении на телефоне.
Вот вся эстафета на одной строке – остальной текст статьи разбирает её по одному бегуну за раз:
Микрофон → захват → фильтр верхних частот → эхоподавление → шумоподавление → управление уровнем усиления → кодирование (Opus) → упаковка (RTP) → передача по сети → буфер джиттера (NetEQ) → декодирование → воспроизведение → динамик.
Конвейер чётко делится на три области, и полезно держать их в голове раздельно. Сторона отправки (от захвата до упаковки) работает на устройстве говорящего и в основном занимается очисткой звука и его сжатием. Сеть – это часть, которую никто не контролирует, где пакеты задерживаются, перемешиваются и теряются. Сторона приёма (от буфера джиттера до воспроизведения) работает на устройстве слушателя и в основном направлена на то, чтобы скрыть, что с звуком сделала сеть.
Сторона отправки: захват и очистка
Захват – превращаем воздух в числа
Микрофон – это датчик, преобразующий давление воздуха в переменное напряжение. Затем аналого-цифровой преобразователь звуковой карты измеряет это напряжение тысячи раз в секунду и записывает каждое значение как число. WebRTC работает с частотой дискретизации 48 кГц – 48 000 измерений в секунду, – поскольку именно на этой частоте нативно функционирует его кодек Opus. Частота дискретизации – как тиканье часов: 48 000 раз в секунду система спрашивает микрофон: «Какой сейчас уровень сигнала?» – и фиксирует ответ. (Если вы впервые сталкиваетесь с понятием частоты дискретизации, об этом подробно рассказано в базовой статье: Частота дискретизации: 44.1, 48, 96, 192 кГц и почему 48 кГц победила в видео.)
В браузере захват начинается, когда ваш код вызывает getUserMedia – функцию из стандарта W3C Media Capture and Streams, которая запрашивает у пользователя доступ к микрофону и возвращает живую аудиодорожку. За этим одним вызовом прячется многое: выбор устройства, путь звука через операционную систему и ограничения, которые можно запросить (частота дискретизации, число каналов и включена ли встроенная очистка браузера).
// Запрашиваем микрофон со встроенной очисткой WebRTC.
// echoCancellation, noiseSuppression, autoGainControl — три
// процессора стороны захвата, описанные ниже; в браузерах по умолчанию true.
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
}
});Эти три флага – echoCancellation, noiseSuppression, autoGainControl – активируют три процессора, выполняющие основную работу. Они включены по умолчанию в каждом браузере, поэтому обычный видеозвонок звучит вполне приемлемо даже на ноутбуке с громкоговорителями, работающими на полную мощность.
Модуль обработки аудио: один блок – несколько задач
В libwebrtc очистка звука на стороне захвата реализована в компоненте Audio Processing Module (APM). По словам разработчиков проекта, его задача – применять эффекты улучшения речи к сигналу с микрофона. К числу канонических примеров относятся эхоподавление, шумоподавление и автоматический контроль усиления. APM обрабатывает захваченный аудиосигнал строго в определённом порядке, и этот порядок принципиален: high-pass filter → акустическое эхоподавление (AEC3) → шумоподавление (NS) → автоматический контроль усиления (AGC2). Каждая стадия предполагает, что предыдущая уже выполнена, поэтому изменение последовательности нарушило бы допущения, на которых основана работа каждой из них.
Фильтр высоких частот – убираем низкочастотный гул
Первая стадия – фильтр верхних частот (high-pass filter), который удаляет энергию самых низких частот: стук по столу, гул кондиционера, рокот от вибраций ниже диапазона речи. Человеческая речь находится примерно в диапазоне от 80 Гц до 8 кГц; энергия ниже этой полосы не несёт полезной информации о голосе и лишь усложняет работу последующих этапов. Фильтр верхних частот – как сито, пропускающее высокие частоты и задерживающее низкочастотный рокот. Удалив его на ранней стадии, вы обеспечиваете эхоподавителю и шумоподавителю более чистый сигнал для анализа.
Акустическое эхоподавление – самая сложная задача конвейера
Эхо – самый разрушительный аудиодефект в звонке, и его причина – в геометрии. Когда вы используете громкую связь, звук из динамика – голос собеседника – снова попадает в ваш микрофон и возвращается обратно. Тот, кто говорит на другом конце, слышит свой собственный голос с задержкой, вызванной круговым путём сигнала, что крайне некомфортно и почти делает разговор невозможным.
Акустическое эхоподавление (AEC, acoustic echo cancellation) решает эту задачу хитростью. Устройство заранее знает, какой сигнал будет проигрываться через динамик, поскольку именно оно его и генерирует. Этот известный сигнал называется эталоном (reference). Эхоподавитель берёт эталон, моделирует, как комната и путь «динамик → микрофон» искажают и задерживают его, и вычитает предсказанное эхо из сигнала микрофона. В идеале остаётся только ваш голос. Современный эхоподавитель WebRTC называется AEC3 и используется в конвейере по умолчанию. Пунктирная петля на Рис. 1 – это как раз эталонный сигнал, идущий из тракта воспроизведения обратно к подавителю.
Под капотом – адаптивный фильтр. Подавитель не знает комнату заранее, поэтому он «угадывает», сравнивает предсказание с реальностью и подстраивается тысячи раз в секунду, пока предсказание не совпадёт. Когда вы двигаете ноутбук, прикрываете микрофон или кто-то проходит между вами и динамиком, комната меняется, и фильтру приходится заново сходиться – поэтому эхо иногда «протекает» на секунду после возмущения.
«Частая ловушка: проблема double-talk. Эхоподавление работает легко, когда говорит только один человек. Но задача усложняется, когда оба собеседника говорят одновременно – это и есть «double-talk». В этом случае микрофон фиксирует и ваш голос, и эхо голоса собеседника, и подавителю нужно убрать эхо, не повредив при этом вашу речь. Слабый алгоритм может «срезать» ваши слова, когда на другом конце тоже говорят. Это – главный симптом, на который обращают внимание инженеры. Bluetooth-громкая связь усугубляет проблему, добавляя непредсказуемую задержку в аудиопетлю. Подробно разбираем тему в статье Акустическое эхоподавление (AEC): как это на самом деле работает.»
Шумоподавление – выделить голос из фонового шума
Шумоподавление (NS, noise suppression) выполняется после эхоподавления. Его задача – ослабить постоянный фоновый шум (звук вентиляторов, трафик, щелчки клавиатуры, гул в кафе), не затрагивая речь. Классический метод шумоподавления оценивает уровень шума в паузах между словами, а затем вычитает эту оценку из всего аудиосигнала. Современные системы на основе глубокого обучения, такие как RNNoise и коммерческие решения вроде Krisp и NVIDIA RTX Voice, работают иначе: они учатся распознавать речь и оставляют только её, что позволяет эффективно устранять непостоянный шум – например, лай собаки или хлопок двери. Главный компромисс остаётся неизменным: чрезмерное подавление приводит к тому, что голос начинает звучать «под водой». Полный обзор всех техник – в статье Шумоподавление: классический NS, RNNoise, Krisp, NVIDIA RTX Voice.
Автоматический контроль усиления – поддерживаем стабильный уровень
Последняя стадия APM – автоматический контроль усиления (AGC, automatic gain control), и в текущей версии WebRTC используется AGC2. Люди сидят на разном расстоянии от микрофона и говорят с разной громкостью; задача AGC – привести каждый голос к стабильному целевому уровню, чтобы слушателю не приходилось постоянно регулировать громкость. AGC2 объединяет несколько контроллеров: контроллер входного уровня, который управляет усилением микрофона в операционной системе, адаптивное цифровое усиление, отслеживающее уровень речи, и фиксированный лимитер, срабатывающий при резких пиках. Режим сбоя здесь – «погоня AGC»: если петля управления слишком агрессивна, она заметно «качает» уровень вверх-вниз, и фоновый шум тихой комнаты становится слышимым каждый раз, когда вы делаете паузу. Подробнее о настройке – в статье Автоматический контроль усиления (AGC): держим уровень голоса стабильным.
Сторона отправки: кодирование и упаковка
Кодирование – сжимаем фрейм кодеком Opus
После очистки звук остаётся «сырым» – около 768 килобит в секунду на один 48-кГц 16-битный канал. Передавать такие данные по мобильной сети было бы расточительно и ненадёжно, поэтому кодек сжимает каждый фрейм. Кодек WebRTC по умолчанию – Opus, описанный в IETF RFC 6716, и именно он обеспечивает высокое качество звука WebRTC даже при низких битрейтах. Opus работает с фреймами от 2,5 до 60 мс, поддерживает битрейты от 6 до 510 кбит/с и использует частоту дискретизации 48 кГц. На практике WebRTC упаковывает Opus во фреймы по 20 мс – это стандартный компромисс между задержкой (чем короче, тем лучше) и эффективностью сжатия (чем длиннее, тем лучше сжимается).
Opus необычен тем, что в нём объединены два кодека – речевой (SILK) и музыкальный (CELT) – с переключателем, который выбирает нужный или смешивает их. Поэтому один и тот же кодек одинаково хорошо справляется и с шёпотом телемедицинской консультации, и с фоновой музыкой в стриме лайв-шопинга. Две особенности Opus важны для конвейера и снова проявятся на стороне приёма:
- In-band forward error correction (FEC). Opus может встроить низкобитрейтную копию предыдущего фрейма в текущий пакет – такие фреймы называются LBRR. Если один пакет теряется, следующий содержит приближённую копию пропущенного, и декодер может восстановить пропуск. FEC работает только в речевом режиме Opus.
- Discontinuous transmission (DTX). Когда пользователь перестаёт говорить, Opus прекращает отправлять полные пакеты и вместо этого раз в 400 мс передаёт крошечный «дескриптор тишины». Это снижает трафик от молчащего участника примерно на 85–90 %, поскольку в звонке между двумя людьми каждый молчит около половины времени.
Глубокий разбор самого кодека – в статье Opus: открытый кодек, который съел WebRTC.
Упаковка: оборачиваем фрейм для сети в протокол RTP
Сжатый фрейм ещё не готов к передаче. Ему нужна адресная этикетка, чтобы приёмник мог собрать поток в правильном порядке и вовремя. Эта этикетка – заголовок Real-time Transport Protocol, определённый в IETF RFC 3550, а сам процесс обёртывания называется упаковкой (packetization). Правила, по которым фреймы Opus помещаются в RTP-пакеты, описаны в IETF RFC 7587. Три поля в каждом RTP-заголовке выполняют основную работу:
- Номер последовательности, увеличивающийся на единицу с каждым пакетом, чтобы приёмник мог обнаружить потерю или перестановку пакетов.
- Метка времени, указывающая положение фрейма в медиачасах, чтобы приёмник мог корректно воспроизвести звук. (Важно: RTP-метка времени – это не реальное время; синхронизация аудио и видео осуществляется с помощью отдельного отчёта, который подробно рассматривается в статье RTP timestamps, RTCP sender reports и синхронизация по NTP.)
- SSRC (идентификатор источника синхронизации), определяющий, к какому потоку относится пакет, чтобы клиент, принимающий звук от нескольких участников, мог различать их.
Затем пакет шифруется – WebRTC требует использовать SRTP с защищённым рукопожатием DTLS (IETF RFC 5764) – и отправляется в сеть. На этом отправка завершена: пакет в пути.
Сеть: часть, которую вы не контролируете
Как только пакет покидает устройство, с ним могут произойти три вещи – и все они нежелательны. Он может быть задержан сильнее, чем предыдущие, из-за чего равномерно отправленные пакеты придут сгустками – это называется джиттер. Он может быть переставлен и прийти позже, чем пакет, отправленный позже. И он может быть полностью потерян – например, отброшен перегруженным маршрутизатором или слабым Wi-Fi. Типичное мобильное соединение теряет небольшой процент пакетов и колеблется задержка на десятки миллисекунд; плохое – намного хуже.
Сторона отправки не может это исправить, ведь к моменту потери пакета его уже не существует. Всё, что делает сторона приёма, – это устранение последствий: сглаживание джиттера, восстановление порядка пришедших не по порядку пакетов и генерация правдоподобного звука вместо утерянного. Поэтому именно на стороне приёма, а не отправки, сосредоточена большая часть аудиоумений WebRTC.
Сторона приёма: NetEQ – мозг аудио WebRTC
Почему буфер неизбежен
Пакеты приходят нерегулярно, но воспроизведение должно быть идеально стабильным – динамике нужны свежие 10 мс звука каждые 10 мс, бесконечно и без пауз. Буфер джиттера – это устройство, которое соединяет эти два мира. Он работает как зона ожидания у выхода на посадку: пассажиры (пакеты) прибывают в непредсказуемое время, а рейсы (воспроизведение) улетают строго по расписанию, поэтому зона ожидания держит людей, пока не наступит время посадки. Буфер хранит приходящие пакеты ровно столько, чтобы выпускать их вовремя и в правильном порядке.
Трудная часть – определить, сколько ждать. Если ждать слишком мало, слегка опоздавший пакет может не попасть в слот и будет засчитан как потерянный. Если ждать слишком долго – появится заметная задержка в разговоре, и люди начнут перебивать друг друга. Фиксированный буфер не может найти баланс, потому что условия в сети меняются от минуты к минуте. Решение – адаптивный буфер джиттера, который постоянно анализирует состояние сети и подстраивает целевую задержку.
Что делает NetEQ
Адаптивный буфер джиттера WebRTC называется NetEQ – в описании проекта он представлен как «буфер джиттера аудио и средство сокрытия потерь пакетов», непрерывно оптимизирующий задержку буферизации под текущие сетевые условия, чтобы обеспечить плавное воспроизведение с минимально возможной задержкой. У него две «двери». Пакеты поступают через InsertPacket, где отбрасываются те, что пришли слишком поздно, чтобы быть полезными, а остальные сохраняются с обновлением статистики о времени поступления. Звук выдаётся через GetAudio – устройство воспроизведения вызывает этот интерфейс, чтобы получить ровно 10 мс аудиоданных в нужный момент.
Приём, позволяющий NetEQ держать задержку низкой и избегать пробелов, заключается в том, что он может незаметно растягивать или сжимать звук во времени. Когда буфер заполняется (сеть ускорилась), NetEQ воспроизводит декодированный фрейм чуть быстрее, чтобы избавиться от избытка. Когда буфер опустошается (сеть замедлилась), он играет чуть медленнее, чтобы выиграть время. Эти корректировки настолько малы, что остаются неслышимыми. Каждый раз, когда устройство воспроизведения запрашивает звук, NetEQ возвращает результат одной из шести операций:
| Операция NetEQ | Когда происходит | Что слышит слушатель |
|---|---|---|
| Normal | Пакет доступен, уровень буфера в норме | Чистый декодированный звук |
| Acceleration | Буфер слишком полон (сеть ускорилась) | Звук играется чуть быстрее, чтобы слить буфер |
| Preemptive expand | Буфер слишком пуст (сеть замедлилась) | Звук играется чуть медленнее, чтобы добавить задержку |
| Expand | Пакета нет – он опоздал или потерян | Сокрытие потери: выдуманный звук |
| Merge | Реальный пакет приходит сразу после выдуманного | Выдуманный звук гладко сшивается с реальным фреймом |
| Comfort noise (CNG) | Говорящий молчит и использует DTX | Мягкий синтетический фоновый шум вместо мёртвой тишины |
Таблица 1. Шесть операций воспроизведения NetEQ. Первые три управляют задержкой, последние три – маскируют потерю пакетов и тишину. Источник: документация WebRTC NetEQ.
Прячем потерянные пакеты: PLC, FEC и RED
Когда пакет так и не приходит, операция Expand в NetEQ генерирует скрытие потери пакета (packet loss concealment): она экстраполирует звук на основе уже имеющихся данных, повторяя и постепенно затухая недавнюю форму волны, чтобы пробел звучал как короткое продолжение, а не как щелчок или тишина. Такой подход хорошо работает при потере одного пакета, но ухудшается с ростом числа потерь.
NetEQ также поддерживает две схемы избыточности со стороны отправителя. Если поток Opus содержит in-band FEC, NetEQ может восстановить потерянный фрейм на основе низкобитрейтной копии из следующего пакета. В документации WebRTC NetEQ указано, что среди его функций – «исправление ошибок в прямом канале (RED или in-band FEC кодека)», а также отслеживание потерь с помощью отрицательного подтверждения (NACK) и синхронизация аудио и видео: NetEQ позволяет намеренно добавлять задержку, чтобы синхронизировать звук с видео. RED, описанный в IETF RFC 2198, представляет собой общую схему, при которой копии старых фреймов упаковываются в новые пакеты. Проблемы выбора между PLC, FEC и RED – когда каждый из методов действительно улучшает качество звонка, а когда лишь увеличивает трафик без пользы – подробно рассматриваются в статьях Сокрытие потери пакетов (PLC): прячем пропавшие фреймы и Forward error correction (FEC), in-band FEC и избыточность RED.
Декодирование и воспроизведение
Как только NetEQ определяет, какие 10 мс звука нужно воспроизвести, декодер Opus преобразует сжатый фрейм обратно в сырые отсчёты, а этап воспроизведения передаёт эти отсчёты аудиовыходу операционной системы, который приводит в действие динамик или наушники. Эстафета завершена: волна, начавшаяся в одной комнате, теперь звучит в другой.
Бюджет задержки, который можно повесить на стену
Каждый прыжок требует времени, и именно суммарная задержка определяет, насколько разговор воспринимается естественно. Отраслевое правило большого пальца, основанное на старом руководстве телефонных сетей (рекомендация ITU-T G.114), гласит: односторонняя задержка «от рта до уха» ниже примерно 150 мс практически незаметна, до 200 мс – комфортна для большинства разговоров, а при задержке свыше 400 мс люди начинают перебивать друг друга. Вот примерный бюджет для качественного звонка WebRTC; реальные значения зависят от сети и устройства.
| Стадия | Типичный односторонний вклад | Почему |
|---|---|---|
| Захват + очистка APM | ~10–20 мс | Буферизация плюс обработка эха/шума/усиления |
| Кодирование Opus | ~20 мс | Один фрейм 20 мс нужно собрать до кодирования |
| Упаковка + отправка | ~1 мс | RTP-заголовок, шифрование, передача в ОС |
| Сеть (в одну сторону) | ~10–150 мс | Доминирующий и наименее контролируемый член |
| Буфер джиттера (NetEQ) | ~20–100 мс | Адаптивная задержка, которую он выбирает для поглощения джиттера |
| Декодирование + воспроизведение | ~10–30 мс | Декодирование плюс собственный буфер выходного устройства |
| Итого (хорошая сеть) | ~70–120 мс | Уверенно ниже цели 200 мс |
Таблица 2. Представительный односторонний бюджет задержки для аудиозвонка WebRTC. Два компонента, которые вы контролируете в наименьшей степени – сеть и буфер джиттера – одновременно являются и самыми крупными, поэтому оптимизация на стороне приёмника важнее, чем на стороне отправителя. Сложим контролируемые компоненты: 15 + 20 + 1 + 10 + 10 = 56 мс задержки на стороне устройства при хорошей сети, остальное приходится на NetEQ и провод.
Арифметику стоит проговорить вслух хотя бы раз. На хорошей локальной сети контролируемые задержки на стороне устройства складываются примерно так: 15 мс (захват) + 20 мс (кодирование) + 1 мс (упаковка) + 10 мс (воспроизведение) = 46 мс, плюс небольшая задержка на декодирование. Сеть добавляет ещё 10–30 мс в каждую сторону, а NetEQ – 20–60 мс намеренной буферизации. В итоге общая задержка в одну сторону составляет около 90–120 мс – уверенно в пределах комфортного диапазона. Когда сеть начинает деградировать, NetEQ увеличивает свой буфер, чтобы сохранить плавность воспроизведения, и именно эта задержка ощущается при плохом соединении.
Где здесь Фора Софт
Мы создаём продукты, судьба которых зависит от этого конвейера: платформы телемедицины, где врач должен чётко слышать пациента; инструменты видеоконференций и e-learning, где десятки людей находятся в одной виртуальной комнате; приложения контакт-центров и лайв-шопинга, где каждая секунда звука – это и есть сам продукт. В этих проектах мы используем те же этапы, что описаны в статье: выбираем, полагаться ли на Opus FEC или RED для аудитории с потерями в мобильной сети, подбираем размер NetEQ под допустимую задержку для конкретного сценария и решаем, когда микшировать звук на сервере, а когда передавать его без изменений. Конвейер универсален; задача инженерии – настроить его так, чтобы он звучал правильно для конкретного продукта и конкретной сети.
Главное
- Конвейер – это как эстафета: захват, очистка, кодирование, упаковка, отправка, буферизация, декодирование, воспроизведение.
- Очистка на стороне отправки выполняется в строгом порядке: high-pass filter, AEC3, шумоподавление, AGC2.
- Opus на фреймах 20 мс – кодек по умолчанию; FEC и DTX помогают компенсировать потери пакетов и экономить трафик.
- Сеть может задерживать, переупорядочивать и терять пакеты – всё устранение последствий ложится на сторону приёма.
- NetEQ адаптирует размер буфера и применяет шесть операций, балансируя между низкой задержкой и плавным воспроизведением.
- Старайтесь держать одностороннюю задержку «ото рта до уха» ниже ~200 мс; основную долю в ней составляют задержка сети и буфер джиттера.