Аудио в SFU, MCU и P2P

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

Кратко

Когда в звонке больше двух человек, что-то посередине должно решить, как голос каждого участника дойдёт до всех остальных, и вариантов ровно три: отправлять каждый поток напрямую каждому слушателю (P2P), поручить серверу смешать все голоса в один общий поток (MCU) или поручить серверу пересылать каждый голос без изменений тем, кто должен его услышать (SFU). Именно для аудио у SFU есть приём, который делает его современным выбором по умолчанию: он умеет читать крошечное число «насколько я громкий» в каждом пакете и пересылать только тех, кто реально говорит, ни разу не декодируя звук. MCU выдаёт один чистый смешанный поток, который воспроизведёт любое устройство и который легко записать, но требует много серверных ресурсов и добавляет задержку, потому что декодирует и заново кодирует всё. Правильный выбор для вашего продукта зависит от трёх вещей – сколько человек говорит одновременно, нужна ли запись или транскрипция, и сколько вы готовы тратить на серверы, – и эта статья даёт цифры, чтобы решить.

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

Если вы делаете сервис видеоконференций, телемедицинскую платформу, онлайн-класс или контакт-центр, то способ обработки аудио на сервере задаёт ваш месячный счёт за серверы, потолок масштабирования и то, будет ли вебинар на 200 человек звучать чисто или как шумная комната. Решение про аудио тише решения про видео, поэтому команды копируют то, что делает их видеоархитектура, и больше туда не заглядывают – а потом на масштабе обнаруживают, что микширование аудио съедает бюджет CPU, или что выборочная пересылка сломала функцию записи. Эта статья – для продакт-менеджера, основателя или операционного руководителя, которому нужно понять, почему одна архитектура смешивает аудио, а другая пересылает его, чтобы читать архитектурное предложение, задавать инженеру правильный вопрос и знать цену каждого выбора. Старший инженер тоже найдёт здесь каждое утверждение со ссылкой на соответствующий IETF RFC или на опубликованное поведение mediasoup, LiveKit и Jitsi. К концу вы будете знать, какой из трёх вариантов выбрать, и когда именно дешёвый выбор перестаёт быть дешёвым.

Один вопрос, на который должен ответить каждый групповой звонок

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

Этот единственный вопрос – объединять голоса или держать их раздельно – и есть вся разница между тремя архитектурами. Топология в контексте звонка реального времени – это форма линий, соединяющих людей в комнате, плюс правило о том, что происходит с аудио по пути вдоль этих линий. Видеоверсия этого вопроса – куда идут линии и что сервер делает с картинкой – подробно разобрана в статье SFU против MCU против Mesh: три топологии WebRTC в нашем разделе про видеостриминг. Здесь мы смотрим только на аудиочасть, потому что аудио отвечает на вопрос «объединять или держать раздельно» совсем иначе, чем видео, и именно в этой разнице живёт большинство сюрпризов.

Полезный образ. Представьте званый ужин. В маленькой группе из четверых все слышат всех напрямую – помощь не нужна. На панельной дискуссии звукорежиссёр берёт микрофон у каждого спикера, сводит их на пульте и подаёт один общий сигнал на колонки в зале. В радиоредакции продюсер держит фид каждого репортёра на отдельном канале и переключается между ними, отправляя в эфир тот, что сейчас в работе. Маленькая группа – это P2P, сводящий пульт – MCU (Multipoint Control Unit, устройство многоточечного управления), переключатель каналов – SFU (Selective Forwarding Unit, блок выборочной пересылки). Компромиссы в софте ложатся почти один в один на эти три реальных схемы.

«Замечание об именах. «P2P» и «Mesh» пересекаются. «P2P» – peer-to-peer – строго означает, что в медиапути нет сервера вообще. С двумя людьми это настоящее прямое соединение. С тремя и более P2P становится Mesh: каждый открывает прямое соединение с каждым. В этой статье мы используем P2P, потому что именно так этот «случай без сервера» называют в SEO и продуктовом мире, и отмечаем, где важно различие с Mesh.»

WebRTC, семейство протоколов, которыми браузеры отправляют живые аудио и видео, не выбирает топологию за вас. Один и тот же JavaScript-API микрофона питает все три схемы – именно поэтому выбор между ними ваша задача, а не браузера.

P2P: каждый отправляет свой голос каждому

Самая простая архитектура не имеет сервера в аудиопути. Каждый участник кодирует свой микрофон по одному разу на каждого слушателя и отправляет напрямую каждому из них. С двумя людьми это одно соединение в каждую сторону и самый чистый возможный аудиопуть – минимальная задержка, никакого промежуточного звена, никаких лишних затрат. Поэтому любой учебник по WebRTC начинается со звонка на двоих по P2P.

Проблема начинается, когда звонок растёт, и проблема эта – исходящая полоса на собственном устройстве человека. В P2P, если в звонке пять человек, ваше устройство должно отправить ваш голос четыре отдельных раза – по одному каждому из четверых – и получить в ответ четыре отдельных голосовых потока. Здесь аудио снисходительнее видео, потому что голосовой поток мал. Типичный голосовой поток WebRTC на кодеке Opus идёт примерно на 24–32 тысячах бит в секунду; даже десять таких – заметно меньше половины мегабита, что осилит любое современное соединение. Так что для одного аудио P2P выдерживает группы крупнее, чем видео-P2P.

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

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

Рисунок 1. Три аудиоархитектуры. P2P держит сервер вне пути целиком. MCU схлопывает все голоса в один смешанный поток. SFU пересылает каждый голос без изменений. Разница между двумя последними – декодирует ли сервер аудио, и эта разница задаёт цену.

MCU: сервер смешивает все голоса в один поток

MCU ставит посередине мощный сервер и даёт ему тяжёлую работу. Каждый участник отправляет свой единственный голосовой поток вверх на сервер. Сервер декодирует каждый обратно в сырой звук, складывает звуки в микс, заново кодирует микс и отправляет этот один общий поток вниз каждому участнику. С точки зрения любого участника он отдаёт один поток и принимает один поток, сколько бы людей ни было в звонке. Звонок на 50 человек для вашего устройства выглядит ровно как звонок на двоих: один поток вверх, один вниз.

Это свойство – главная сила MCU, и о нём стоит сказать точно. Микширование не наивно. Если бы сервер просто складывал аудио всех и отправлял один и тот же микс всем, вы слышали бы эхо собственного голоса. Поэтому правильный аудио-MCU выдаёт чуть разный микс каждому участнику: он отправляет вам голоса всех, кроме вашего собственного. Это называется миксом N-минус-1 – для N участников каждый получает сумму остальных N-1 голосов. Это вычитание и не даёт звонку зациклиться сам на себе.

Чего стоит MCU, в простой арифметике

Цена всего этого удобства – CPU сервера, и арифметика безжалостна. На каждого участника сервер должен выполнить полное декодирование аудио, участие в миксе и полное повторное кодирование. Сделаем это конкретным. Допустим, один цикл «декод плюс кодирование» на сервере стоит некую единицу работы, назовём её одной «аудиозадачей». В звонке на 50 человек сервер декодирует 50 входящих потоков и кодирует до 50 исходящих миксов – порядка 100 аудиозадач, непрерывно, на одну комнату. SFU на той же комнате выполняет ноль аудиозадач, потому что никогда не декодирует. Это не маленькая разница; это разница между сервером, который держит пригоршню комнат, и сервером, который держит сотни.

MCU также добавляет задержку, которой избегают две другие архитектуры. Декодирование, буферизация достаточного объёма аудио для чистого микса, само микширование и повторное кодирование – всё это требует времени. Каждый проход через декодер и кодер добавляет задержку поверх сетевого пути, так что время «рот-до-уха» на MCU стабильно выше, чем на SFU, пересылающем то же аудио. О том, откуда берётся каждая миллисекунда задержки в аудио реального времени, читайте в Конвейер аудио WebRTC целиком.

Где MCU всё-таки выигрывает

При такой цене – зачем MCU вообще выживает? Три причины, и они настоящие.

Во-первых, получателю нужно декодировать всего один поток. Дешёвая ТВ-приставка, шлюз телефонной сети, старый браузер на смарт-ТВ или дозвонившаяся телефонная линия не могут декодировать и смешивать десяток отдельных аудиопотоков – но один воспроизвести могут. Всякий раз, когда среди вашей аудитории есть устройство, способное справиться лишь с одним потоком, MCU подходит естественно. Классический случай – телефонный мост: дозвонившийся по PSTN участник видеовстречи должен получить один смешанный аудиопоток, потому что телефонная линия несёт ровно один.

Во-вторых, один смешанный поток тривиально записать и передать дальше. Если вам нужен один аудиофайл всей встречи, MCU уже его сделал. Мы вернёмся к этому ниже, потому что запись – это место, где архитектуры SFU усложняются.

В-третьих, микс одинаков для всех. Поскольку сервер управляет общим звуком, он может применить единообразную нормализацию громкости, чтобы тихий и громкий спикеры приходили на сопоставимых уровнях. (Громкость между спикерами – отдельная дисциплина, см. Громкость, peak, RMS, LUFS.)

SFU: сервер пересылает голоса, ни разу их не декодируя

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

Поскольку сервер никогда не декодирует, затраты CPU SFU на комнату – доля затрат MCU. Это и есть главная причина, по которой SFU стал выбором по умолчанию для групповых звонков в 2010-х и остаётся им в 2026: он масштабируется на куда большее число одновременных комнат на том же железе. Затраты смещаются с сервера на клиент, потому что теперь ваше устройство принимает и декодирует несколько аудиопотоков вместо одного – но, как мы видели, аудиопотоки малы, и декодировать пригоршню их дёшево на любом современном телефоне.

Приём, который делает аудио SFU рабочим: пересылать только говорящих

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

Вот изящная часть, и это чистая аудиоинженерия. Чтобы решить, кто громче, серверу обычно пришлось бы декодировать каждый поток и измерить его уровень – ровно ту дорогую работу, которой SFU пытается избежать. Выход – небольшое расширение заголовка RTP, определённое в IETF RFC 6464 (Client-to-Mixer Audio Level Indication, декабрь 2011). С ним каждый отправитель ставит на каждый аудиопакет одно число: громкость этого пакета, выраженную значением от 0 до 127 в -dBov (децибелы ниже самого громкого возможного сигнала системы, где 0 – полная шкала, а 127 – цифровая тишина). Число лежит в заголовке пакета, вне зашифрованной области, поэтому сервер может читать громкость каждого пакета, не расшифровывая и не декодируя само аудио. Стандарт даже резервирует один бит, бит «V», чтобы отправитель пометил, считает ли он, что пакет содержит реальную речь.

С этим числом на каждом пакете SFU может ранжировать участников по тому, кто реально говорит, и пересылать только несколько верхних – всё это, ни разу не касаясь звука. Само введение RFC 6464 формулирует цель прямо: оно позволяет серверу «пересылать только несколько самых громких аудиопотоков, не требуя декодировать и измерять каждый получаемый поток». Комплементарный стандарт, RFC 6465 (Mixer-to-Client Audio Level Indication, декабрь 2011), позволяет микшеру сообщить каждому клиенту уровни отдельных голосов, вошедших в микс, – так интерфейс клиента может подсветить активного спикера, даже когда аудио приходит уже смешанным.

Определение доминирующего спикера: как превратить числа громкости в решение

Прочитать число громкости – шаг один. Решить, кто «активный спикер» – чтобы интерфейс подсветил его, а SFU переслал, – шаг два, и он не так прост, как «кто громче прямо сейчас». Кашель, упавший телефон или один громкий слог не должны красть фокус. RFC 6464 сам предупреждает, что серверам «не следует строить решения о пересылке аудио прямо на попакетной информации об уровне», а вместо этого надо сглаживать уровни во времени, прежде чем действовать.

Самое широко применяемое решение – алгоритм Иланы Волфин и Исраэля Коэна, Dominant Speaker Identification for Multipoint Videoconferencing, который анализирует историю уровней каждого участника по коротким, средним и длинным временным окнам, чтобы решить, кто действительно держит слово, – всё это из чисел громкости RFC 6464, без декодирования аудио. Это алгоритм за ActiveSpeakerObserver в mediasoup (добавлен в mediasoup 3.8.0) и за логикой доминирующего спикера в Jitsi. В результате получается стабильный сигнал «активного спикера», который SFU использует и для пересылки нужных потоков, и для подсветки в интерфейсе.

Наглядный пример делает экономию очевидной. На собрании-таунхолле на 200 человек, где один выступает, а остальные слушают, MCU вечно декодировал бы и смешивал все 200 потоков. SFU, использующий уровни RFC 6464 плюс определение доминирующего спикера, пересылает по сути один поток – выступающего – всем 199 слушателям, не декодируя на сервере ничего. Разница в полосе и CPU не постепенная; это разница между жизнеспособным продуктом и нежизнеспособным.

Рисунок 2. Как SFU пересылает только говорящих. Каждый отправитель ставит свою громкость на каждый пакет (RFC 6464). Сервер ранжирует спикеров по этим числам – никогда не декодируя звук – и пересылает только активных, опираясь на алгоритм доминирующего спикера, игнорирующий краткие шумы.

Сравнение бок о бок

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

КритерийP2PMCUSFU
Сервер в аудиопутиНетДа, тяжёлыйДа, лёгкий
Сервер декодирует аудиоКаждый потокНикогда
CPU сервера на комнатуНольОчень высокийНизкий
Потоков клиент отдаётПо одному на каждогоОдинОдин
Потоков клиент принимаетПо одному на каждогоОдин (микс)По одному на спикера
Нагрузка декодирования у клиентаСредняяМинимальная (один)Низкая–средняя
Добавленная задержкаМинимальная (напрямую)МаксимальнаяНизкая
Масштаб на большие комнатыНетОграничен CPUДа (по умолчанию)
Простая запись в один файлНетДа (микс готов)Сложнее (нужен микс)
«Тупые» получатели (PSTN, приставка)НетДаНет
Поконтрольно (mute, уровни, раскладка)Теряется в миксеПолный

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

Частая ошибка: считать, что запись на SFU бесплатна

Вот ловушка, удивляющая команды чаще всего, и это специфично для аудио. Поскольку SFU держит каждый голос на отдельном потоке, единого общего аудиопотока в системе нет нигде. Это прекрасно для живого звонка – каждый спикер остаётся независимым, так что интерфейс может заглушить одного человека, показать поспикерные уровни или применить пространственное аудио. Но в момент, когда заказчик спрашивает «можно записать встречу и прислать MP3 потом?», SFU нечего ему дать. Микса нет. Чтобы сделать одну запись, придётся поднять отдельный процесс, который подпишется на каждый поток, декодирует их все, сведёт и закодирует результат, – что иронично, ровно та работа, которую MCU делал всё это время.

Поэтому зрелые продукты на SFU держат выделенный сервис записи, или «egress», рядом с пересыльщиком. LiveKit, например, – это SFU, который не декодирует аудио на сервере во время живого звонка; чтобы записать, вы подписываетесь на аудиодорожки, декодируете кадры Opus, агрегируете их в сырой PCM и кодируете файл. Урок для планирования: SFU не делает запись бесплатной, и вам придётся заложить CPU на микширование, от которого вы думали уйти. Полный путь записи и транскрипции – отдельная тема, см. Конвейеры записи и транскрипции.

Вторая, тихая ловушка: DTX сбивает наивные читатели уровня. Прерывистая передача Opus (DTX) позволяет тихому микрофону упасть с примерно 24–32 кбит/с до примерно 1 кбит/с, почти ничего не отправляя во время тишины. Это реальная экономия полосы – но наблюдатель уровня, ожидающий пакет каждые 20 миллисекунд, может прочитать паузы как «спикер замолчал», потому что пакеты попросту не приходят. Трекер задач самого LiveKit документирует именно это взаимодействие его наблюдателя уровня с Opus DTX. Если ваш интерфейс активного спикера мерцает, когда люди затихают, тайминг DTX – первое место, куда смотреть. О том, как работают DTX и подавление тишины, см. Детектор речевой активности (VAD) и прерывистая передача (DTX).

Как выбирают реальные продукты

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

Многие продакшн-системы – гибриды. Звонок может начаться как P2P на двоих, затем повыситься до SFU в момент, когда присоединяется третий. Конференция на SFU может поднять небольшой микшер в стиле MCU только для ветки дозвона по PSTN, сводя комнату в один поток для телефонного абонента, пока все остальные остаются на независимых потоках SFU. Архитектура – не религия; это решение на комнату, иногда на участника, и заголовок уровня аудио из RFC 6464 – то, что делает SFU-половину каждого гибрида доступной по цене.

Дерево решений, простыми словами. Двое и запись не нужна? P2P. Получатели, способные воспроизвести лишь один поток, или жёсткая потребность в одной общей записи с минимумом инженерии? MCU. Всё остальное – а это большинство продуктов – SFU, с отдельным сервисом egress, если нужны записи. Схема ниже проходит ту же логику сверху вниз.

Рисунок 3. Выбор аудиоархитектуры. Двое: P2P. Получатель, способный воспроизвести лишь один поток: MCU. Общая запись с минимумом усилий: MCU. Всё остальное: SFU, с отдельным сервисом egress, когда нужны записи.

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

Мы встраиваем аудио реального времени в видеоконференции, телемедицину, онлайн-обучение и контакт-центры с 2005 года, и вопрос архитектуры выше – один из первых, что мы решаем на каждом проекте. Телемедицинская консультация между одним врачом и одним пациентом часто лучше как чистый звонок P2P; виртуальный класс с одним учителем и сорока учениками – это SFU, пересылающий учителя плюс тех немногих учеников, кто снял mute; вебинар, который должен ещё и принимать телефонные дозвоны, нуждается в SFU с небольшим мостом MCU для телефонной ветки. Мы выбираем топологию под форму звонка, требования к записи и набор устройств, которыми аудитория реально пользуется, – а не под моду. Когда продукту нужны и большой масштаб, и чистые записи, мы планируем CPU на egress-микширование заранее, потому что видели, что бывает с командами, которые узнают про эту цену за неделю до запуска.

Ключевые выводы

  • Есть три аудиоархитектуры: P2P (без сервера), MCU (сервер смешивает), SFU (сервер пересылает).
  • SFU пересылает только самых громких спикеров по числам уровня RFC 6464, не декодируя звук.
  • MCU отправляет один общий поток, но платит тяжёлым CPU сервера и добавляет больше всего задержки.
  • P2P лучше для один на один и крошечных звонков; свыше четырёх человек нужен сервер.
  • SFU – выбор по умолчанию в 2026; его слабое место – создание одной записи.
  • Запись на SFU не бесплатна – нужен отдельный сервис микширования, который надо заложить.

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

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

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