Групповые звонки при масштабировании: 50, 500, 5000 участников

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

Кратко

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

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

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

Число, которое решает всё: кто кого слышит

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

Подставим числа, потому что форма роста и есть вся суть. Формула – на первой строке, умножение – на второй, результат – на третьей.

  • 5 человек: 5 × 4 = 20 доставок голоса.
  • 50 человек: 50 × 49 = 2 450 доставок.
  • 500 человек: 500 × 499 = 249 500 доставок.
  • 5 000 человек: 5 000 × 4 999 = 24 995 000 доставок.

Обратите внимание, что произошло. Переход от 5 к 50 участникам – в десять раз больше людей – дал не в десять, а примерно в 122 раза больше доставок. Это квадратичный рост: работа растёт как квадрат числа людей, поэтому удвоение комнаты почти учетверяет нагрузку. Инженеры Jitsi в статье 2015 года о масштабе конференций сформулировали ту же мысль прямо: в системе, где каждая точка отправляет медиа всем остальным, «требуемые ресурсы (CPU, пропускная способность сети) растут квадратично с числом точек», и такая архитектура «плохо масштабируется, когда число участников значительно (≈ 50)». Пятьдесят – это число, на котором наивный подход начинает болеть; в заголовке этой статьи оно не случайно.

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

«О различии «активный» и «пассивный». В этой статье активный участник – тот, чей микрофон включён и может быть услышан; пассивный – только слушает. У тауунхолла на 5 000 человек обычно 3 активных и 4 997 пассивных. Стоимость аудио задаёт число активных, а не общее число – и разделение этих двух величин первое, что делает грамотно спроектированный большой звонок.»

Семейство протоколов, которым браузеры передают живое аудио, WebRTC, не решает за вас проблему масштабирования. Оно даёт трубу; решать, какое аудио по ней течёт на каждом масштабе, – работа вашей архитектуры. Чтобы понять три кирпичика, которые мы комбинируем ниже – отправлять напрямую, пересылать и смешивать, – начните с сопутствующей статьи Аудио в SFU, MCU и P2P, где каждый из них разобран по отдельности. Эта статья – о том, что происходит, когда вы доводите их до 50, 500 и 5 000.

Режим первый – до ~50 человек: пересылаем всё

Первый режим – это малый и средний групповой звонок: дейли команды, класс, заседание совета. Здесь правильный ответ – Selective Forwarding Unit, SFU: сервер, который принимает голос каждого участника и пересылает его дальше, ни разу не декодируя звук. Каждый участник загружает один поток и скачивает по одному потоку на каждого, кого слушает. Работа сервера лёгкая, потому что он никогда не декодирует аудио; его основная стоимость – шифрование, к которому мы вернёмся.

Вот аудиоспецифичный факт, который удивляет даже опытных видеоинженеров: в этом режиме всё аудио пересылается всем и всегда. Знаменитый приём масштабирования Last-N, при котором сервер пересылает лишь несколько самых релевантных видеопотоков, а остальные ставит на паузу, придуман для видео, и в исходной статье Jitsi прямо сказано: «схема Last N применяется только к пересылке видеопотоков, но не аудио; аудио со всех точек пересылается всегда». Причина в том, что аудио дёшево и чувствительно к задержке. Голосовой поток на кодеке Opus занимает около 24–32 тысяч бит в секунду – доля от размера видеопотока, – и если придержать чьё-то аудио, решая, «релевантен» ли он, вы срежете первое слово. Поэтому в звонке на 50 человек сервер пересылает каждый голос каждому слушателю, и это нормально: потоки маленькие, и клиент способен декодировать пару десятков.

Куда же тогда уходит стоимость? В два места. Первое – нагрузка шифрования на сервере. WebRTC требует шифровать каждый медиапакет на участке до сервера, поэтому SFU должен расшифровать каждый входящий пакет и заново зашифровать его для каждой исходящей копии. Замеры Jitsi показали рост CPU примерно в шаге с общим битрейтом, «в основном за счёт расшифровки и повторного шифрования RTP-пакетов» – а не самого аудио, которое никогда не декодируется. Второе – нагрузка декодирования и микширования на клиенте. Ваше устройство декодирует каждый входящий голос и сводит их для динамиков, и эта работа растёт с числом активных говорящих.

Приём, который масштабирует аудио, не ломая его

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

Первый – прерывистая передача (DTX): тихий микрофон фактически перестаёт отправлять данные. Opus DTX роняет молчащего участника с ~24–32 kbit/s до примерно 1 kbit/s, отправляя лишь крошечную подсказку «комфортного шума» раз в несколько сотен миллисекунд. Поэтому в звонке на 50 человек, где говорят трое, полноскоростное аудио шлют только эти трое; остальные 47 на проводе почти молчат. Реальную аудионагрузку комнаты задают активные говорящие, а не общее число. (Как работают DTX и комфортный шум, см. Детектор речевой активности (VAD) и прерывистая передача (DTX).)

Второй – осведомлённость об активном говорящем. Каждый отправитель проставляет громкость каждого аудиопакета в маленькое поле RTP-заголовка, определённое в IETF RFC 6464 – число от 0 до 127 в децибелах ниже полной шкалы, – и сервер читает его без декодирования звука. Хотя сервер пересылает всё аудио, он использует эти числа, чтобы сообщить каждому клиенту, кто доминирующий говорящий, – так UI может подсветить его, а клиент может выбрать отрисовку только самых громких. Это фундамент, на котором строится следующий режим.

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

Режим второй – от 50 до ~500 человек: пересылаем только говорящих

После примерно пятидесяти человек меняются две вещи. Комната почти всегда становится асимметричной – несколько докладчиков и большая, в основном замьюченная аудитория, – и даже пересылка аудио только активных говорящих каждому слушателю начинает накапливаться, потому что число слушателей теперь велико. Голос одного докладчика, идущий к 499 слушателям, – это 499 исходящих копий, которые сервер должен зашифровать. С тремя докладчиками это почти 1 500 операций «зашифровать-и-отправить» на каждый аудиокадр, каждые 20 миллисекунд. Аудио всё ещё дёшево маршрутизировать, но стоимостью становится веерная раздача (fan-out).

Архитектура этого режима – по-прежнему SFU, но теперь он пересылает только аудио активных говорящих, а не всех. Сервер ранжирует участников по их числам громкости RFC 6464, определяет тех немногих, кто реально держит слово, и пересылает только эти потоки. Общее собрание на 200 человек, где докладывает один VP, пересылает по сути один аудиопоток 199 слушателям; когда кто-то задаёт вопрос, его поток добавляется в набор пересылаемых, а поток VP может выпасть. Решение о том, кто активный говорящий, – сердце этого режима, и принять его правильно сложнее, чем «кто громче прямо сейчас».

Как выбрать активного говорящего и не ошибиться

Кашель, упавший телефон, одно громкое «ага» в знак согласия – ничто из этого не должно отбирать слово у докладчика. Отраслевой стандарт – алгоритм идентификации доминирующего говорящего, опубликованный Иланой Волфин и Израилем Коэном, который статья Jitsi адаптировала для работы исключительно на уровнях громкости RFC 6464 без декодирования аудио. Вместо реакции на один громкий пакет он оценивает речевую активность каждого участника сразу в трёх временных окнах – мгновенном окне нескольких фонем, среднем окне слова-двух и длинном окне короткого предложения – и объявляет смену говорящего, только когда все три вместе растут на одном канале. Авторы задали явные цели поведения: переходный шум и однословные реплики не должны вызывать смену, смену может запустить только начало настоящего речевого всплеска, а допустимая задержка перехода между говорящими – «до одной секунды». Относительная громкость голоса не должна влиять на шанс быть выбранным – тихого докладчика определяют так же надёжно, как громкого.

Этот алгоритм работает внутри ActiveSpeakerObserver в mediasoup, логики доминирующего говорящего в Jitsi Videobridge и эквивалента в каждом серьёзном SFU. Результат – стабильный сигнал «активного говорящего», который делает сразу две работы: сообщает SFU, какое аудио пересылать, и сообщает каждому клиенту, какую плитку подсветить.

Арифметика выигрыша

Выигрыш – это разница между квадратичным и линейным ростом. Замеры Jitsi оценили её для видео: в конференции на 30 участников пересылка фиксированного числа потоков вместо всех сокращала исходящий битрейт на 72–89% в зависимости от того, сколько пересылалось, превращая квадратичный рост в линейный по числу участников. Для аудио действует та же логика, как только вы пересылаете только активных говорящих: стоимость масштабируется с числом говорящих (которое в любой реальной встрече держится около 1–5), умноженным на число слушателей, а не с квадратом общего числа. Конкретно: в вебинаре на 500 человек с 3 активными говорящими вы пересылаете порядка 3 × 500 = 1 500 доставок аудио вместо 500 × 499 = 249 500, которых потребовала бы «пересылка всего» – сокращение в 166 раз, достигнутое целиком за счёт того, что вы не пересылаете тишину.

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

Рис. 2. Режим два: пересылаем только говорящих. Несколько активных говорящих определяются по числам громкости RFC 6464 и веером раздаются всем слушателям; замьюченное большинство почти ничего не стоит благодаря DTX. Сервер никогда не декодирует аудио – он лишь читает отметку громкости и пересылает.

Режим третий – от 500 до 5 000+ человек: смешиваем немногих, раздаём один

На нескольких тысячах участников стеной становится сама веерная раздача. Отправить аудио трёх говорящих 5 000 слушателям – это 15 000 зашифрованных исходящих потоков на каждый аудиокадр, а один клиент, который ещё и отрисовывает видео, не может комфортно принимать и декодировать несколько отдельных аудиопотоков на фоне всего остального. Выход – перестать слать отдельные потоки и смешать активных говорящих в один общий поток на сервере, а затем отправить этот единственный поток всем. Это подход Multipoint Control Unit – MCU, – применённый хирургически только к аудио и только к активным говорящим.

Механика важна. Сервер декодирует только аудио немногих активных говорящих (не всех 5 000 – остальные молчат и исключены логикой активного говорящего), складывает их в один микс, заново кодирует его один раз и отправляет этот единственный поток каждому слушателю. Слушатель теперь скачивает ровно один аудиопоток независимо от того, 500 в комнате человек или 50 000. Плагин AudioBridge в Janus – производственный пример именно этого: это «аудио-MCU», и «все аудиопотоки смешиваются, а не ретранслируются», поэтому «создаётся единственное PeerConnection независимо от того, сколько участников войдёт в комнату», каждый получает «микс остальных» участников. Стоимость решительно смещается с веерной раздачи в сети на CPU сервера на микширование – но поскольку декодируются только активные немногие, а микс производится один раз, этот CPU ограничен числом говорящих, а не числом слушателей.

Правило N-минус-1 и почему вы никогда не складываете все 5 000

Две детали аудиоинженерии заставляют серверное микширование работать. Первая: микс, отправляемый каждому говорящему, должен исключать его собственный голос – иначе он услышит себя с задержкой в долю секунды. Для N участников каждый получает сумму остальных контрибьюторов – микс N-минус-1. Для чистых слушателей это тривиально (они ничего не вносят, поэтому получают полный микс), а для горстки активных говорящих сервер производит слегка разный микс на каждого. Вторая – и это правило делает масштаб возможным – сервер смешивает только несколько самых громких каналов, а не все. Сложение 5 000 аудиосигналов нагромоздит 5 000 шумовых порогов в шипение громче любого голоса и обойдётся в 5 000 декодирований. Масштабируемый микшер конференции выбирает несколько верхних активных каналов по их уровню аудио и смешивает только их – обычно от трёх до шести голосов. Те же числа громкости RFC 6464, что управляли пересылкой в режиме два, теперь управляют отбором для микширования в режиме три.

Когда одного сервера мало: каскадирование

У одного сервера есть потолок по числу участников независимо от архитектуры. Чтобы выйти за него – в десятки тысяч – производственные системы каскадируют: они запускают много медиасерверов, ретранслирующих друг другу, так что участник, подключённый к серверу во Франкфурте, слышит говорящего, подключённого к серверу в Сан-Паулу, без того, чтобы любой из серверов обслуживал всех участников напрямую. Опубликованная архитектура LiveKit – проработанный пример: пограничные серверы образуют распределённую сетку, координируются через общий слой состояния (Redis) и ретранслируют дорожки между регионами – так платформа заявляет поддержку сессий до 100 000 участников с задержкой между ними менее 100 миллисекунд. Каскадирование не меняет логику аудио внутри комнаты – вы всё так же пересылаете активных говорящих или смешиваете самых громких немногих – оно лишь распределяет веерную раздачу по многим машинам, чтобы ни одна не упёрлась в свой потолок.

Честный финал: на масштабе трансляции это перестаёт быть «звонком»

Есть порог, за которым правильный ответ – признать, что вы больше не ведёте разговор. Запуск продукта на 50 000 зрителей с тремя докладчиками – это трансляция, а не групповой звонок. Стандартный производственный приём – гибрид: несколько докладчиков и приглашённые «на сцену» участники аудитории работают на реальном SFU/MCU для настоящего двустороннего взаимодействия, а пассивная аудитория получает смешанную программу по стримингу вроде Low-Latency HLS через CDN – ровно как живой видеопоток. Интерактивное ядро остаётся маленьким и реальным; массовый пассивный ярус обслуживается инфраструктурой, построенной под массовые пассивные ярусы. Понять, где провести эту черту – сколько людей действительно должны иметь возможность ответить, – самое важное решение по масштабированию, и это продуктовое решение прежде, чем инженерное. Стриминговая половина – отдельная дисциплина; см. Аудио в HLS, DASH, CMAF и Аудио в стриминге с низкой задержкой.

Рис. 3. Выбор по масштабу. До ~50: пересылаем всё. До ~500: пересылаем только активных говорящих. Дальше: смешиваем самых громких немногих и либо каскадируем по серверам, либо, если почти никому не нужно отвечать, раздаём программу веером по стримингу с низкой задержкой.

Три режима рядом

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

Свойство~50 человек~500 человек5 000+ человек
АрхитектураSFU, пересылка всегоSFU, пересылка активныхMCU-микс громких; каскад/гибрид
Сервер декодирует аудио?НикогдаНикогдаТолько активных немногих
Что получает каждый слушательКаждый голосГолоса активных говорящихОдин смешанный поток
Главная стоимостьДекод на клиенте + шифрование на сервереВеерная раздача (шифрование на копию)CPU микса (ограничен говорящими)
Потоков скачивает слушательПо одному на активного говорящегоПо одному на активного говорящегоРовно один (микс)
Добавленная задержкаНаименьшая (только пересылка)Низкая (только пересылка)Выше (декод + микс + перекодирование)
Контроль по говорящему (mute, уровни)ПолныйПолныйТеряется при микшировании
Запись одним файломНужен отдельный миксНужен отдельный миксМикс уже существует
Пример из реальностиКомната Jitsi, mediasoup, LiveKitSFU-вебинар в режиме активного говорящегоJanus AudioBridge; каскад LiveKit; гибрид SFU+LL-HLS

Узор читается ясно, когда вы его видите. По мере роста комнаты стоимость мигрирует с клиента (декодирование многих потоков) на сеть сервера (раздача копий) на CPU сервера (микширование). Каждая миграция покупает вам ещё порядок масштаба и каждая чего-то стоит – обычно контроля по говорящему и немного задержки. Мастерство – менять режим в нужный момент: не слишком рано (теряя простоту пересылки) и не слишком поздно (после инцидента).

Частая ошибка: проектировать под среднее, платить за пик

Вот ловушка, в которую попадает больше всего команд, и она специфична для аудио на масштабе. Аудионагрузку встречи задаёт не общее число участников и даже не среднее число говорящих – её задаёт наихудшее число одновременно говорящих, и это число скачет ровно в неподходящий момент. Общее собрание на 500 человек сидит на одном-двух активных говорящих 55 минут, а потом CEO говорит «есть вопросы?» – и сорок человек размьючиваются разом. Если ваша логика активного говорящего пересылает или смешивает всех, кто на миг стал громким, этот один момент может потребовать в двадцать раз больше аудиоресурсов, чем устойчивое состояние, – и именно в этот момент звонок деградирует на глазах вашей самой важной аудитории.

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

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

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

Мы встраиваем аудио реального времени в видеоконференции, e-learning, телемедицину и продукты для живых событий с 2005 года, и вопрос «сколько участников?» мы фиксируем до того, как написать строку медиакода, потому что он определяет архитектуру сильнее любого другого требования. Виртуальный класс на 30 студентов – это простой SFU, пересылающий преподавателя и тех, кто размьючится; общее собрание компании на 400 человек – это SFU в режиме активного говорящего с лимитом на число говорящих и модерируемой очередью вопросов; запуск продукта на 10 000 участников – это маленькая интерактивная сцена, питающая смешанную программу по стримингу с низкой задержкой для пассивной аудитории. Мы проектируем под пиковый момент одновременных говорящих, а не под комфортное среднее, потому что видели, что делает спайк «есть вопросы?» с системой, рассчитанной на устойчивое состояние. Когда продукту нужны и настоящая интерактивность на масштабе, и чистые записи, мы планируем CPU микширования и путь записи заранее, а не обнаруживаем их под нагрузкой.

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

  • Стоимость аудио растёт как квадрат размера комнаты, если каждый голос идёт всем.
  • До ~50 человек: SFU пересылает всё аудио; DTX держит молчащие микрофоны почти бесплатными.
  • 50–500 человек: пересылайте только активных говорящих по уровням громкости RFC 6464.
  • 5 000+ человек: смешивайте самых громких немногих в один поток; каскадируйте или идите в гибрид.
  • Никогда не складывайте все каналы – смешивайте только верхние три-шесть активных говорящих.
  • Проектируйте под пиковое число одновременно говорящих, а не под среднее.

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

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

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