Содержание статьи +
- Кратко
- Почему это важно
- Что Такое Живой Субтитр
- Часы Доступности, Которые Уже Идут
- SFU, Который У Вас Уже Есть
- Два Места Для Уха: На Клиенте Или На Сервере
- Паттерн Fan-Out, Шаг За Шагом
- Считаем Стоимость Вслух
- Черновик Против Финала: Субтитр, Который Сам Себя Переписывает
- Доставка Текста: Data Channel
- Бюджет Задержки Для Субтитра
- На Клиенте Против На Сервере: Таблица Решения
- Частая Ошибка: Распознавать Каждую Дорожку Всё Время
- Строить На Фреймворке, Покупать API Или И То И То
- Где Здесь Фора Софт
- Ключевые Выводы
- Что Почитать Дальше
Кратко
Живые субтитры в видеозвонке – это бегущий текст того, что говорит каждый участник; его создаёт движок распознавания речи и показывает на экране через секунду-две после произнесённых слов. Дешёвый и согласованный способ их построить – распознавать каждого говорящего один раз на сервере, на медиа-роутере, который и так пересылает звук всех участников (он называется SFU), а затем рассылать готовый текст всем, вместо того чтобы заставлять каждое устройство распознавать звонок самостоятельно. В статье этот паттерн разобран от начала до конца: где SFU «снимает» звук, почему распознавание запускают только для тех, кто реально говорит, как субтитр приходит сначала черновой догадкой, а потом застывает в финальной строке, как текст возвращается клиентам по data channel и во что всё это обходится за встречу. Здесь же проведена черта доступности, на которой вы уже стоите: живые субтитры – требование уровня AA в Web Content Accessibility Guidelines, и правило Министерства юстиции США делает его обязательным для крупных госучреждений с апреля 2026 года.
Почему это важно
Если ваш продукт собирает людей в живой звонок – платформа вебинаров, виртуальный класс, телемед-консультация, трансляция мэрии – субтитры из приятного дополнения превратились в то, чего пользователи ждут, а всё чаще и в то, что требует закон. Инженерный вопрос не в том, добавлять ли их, а в том, где работает распознавание речи: именно этот выбор определяет ваш счёт за облако, выглядят ли субтитры одинаково у всех в комнате и работает ли функция вообще на дешёвом телефоне. Эта статья для продакт-менеджера, основателя или техлида, которому нужно описать функцию субтитров, оценить её стоимость и обсудить её с инженерами, не утонув в аббревиатурах протоколов. К концу вы поймёте единственный архитектурный паттерн, к которому сходятся продакшен-системы конференций, почему он настолько дешевле очевидной альтернативы, и какой дедлайн доступности превращает «надо бы добавить субтитры» в «субтитры нужны к дате». О движках распознавания речи, которые этот паттерн кормит, читайте в разборе потокового ASR – Deepgram, Whisper, AssemblyAI; эта статья – про архитектуру конференции вокруг них.
Что Такое Живой Субтитр
Начнём с самого предмета, потому что за словом «субтитры» прячется полезное различие. Субтитр (caption) – это строка текста, привязанная по времени к речи, которая говорит, что произносится прямо сейчас и кто это произносит. От перевода под видео (subtitle) он отличается замыслом: перевод предполагает, что вы слышите, и переводит слова, тогда как субтитр предполагает, что вы не слышите, и потому ещё помечает говорящего и значимые звуки. В живом звонке практическая форма проста – полоса текста внизу видео, которая обновляется по мере того, как люди говорят, обычно с подписью имени каждого говорящего.
Под большинством субтитров в вебе лежит стандартный формат, и его стоит назвать, потому что он задаёт форму данных. Он называется WebVTT – Web Video Text Tracks – спецификация W3C, базовая единица которой это «cue»: кусок текста с временем начала, временем конца и словами для показа. Cue выглядит примерно так же просто, как и звучит:
00:00:12.000 --> 00:00:15.500
<v Maria>Начнём с бюджета.Этот тег <v Maria> – подпись говорящего: знать, кто сказал, – это часть субтитра, а не украшение. WebVTT спроектирован для заранее записанных файлов, где время каждого cue известно заранее, поэтому живой звонок не отдаёт готовый WebVTT-файл; он создаёт эти cue на лету и стримит их. Но модель в голове работает: дорожка живых субтитров – это последовательность привязанных по времени cue с подписью говорящего, генерируемых по ходу встречи.
Движок, который превращает речь в эти cue, – система автоматического распознавания речи, сокращённо ASR (Automatic Speech Recognition): программа, которая слушает звук и записывает слова. Вся эта статья – о том, где разместить этот движок в многопользовательском звонке и как его вывод попадает на экран каждого.
Часы Доступности, Которые Уже Идут
Сначала о причине срочности. Живые субтитры – это не только любезность по отношению к глухим и слабослышащим; для многих продуктов это требование закона с привязанной датой.
Эталонный стандарт – Web Content Accessibility Guidelines, известный как WCAG, который ведёт W3C. Один из его критериев успеха, под номером 1.2.4 и с заголовком «Captions (Live)», прямо говорит: субтитры должны предоставляться для всего живого аудиоконтента в синхронизированных медиа. Он находится на уровне соответствия AA – среднем из трёх уровней WCAG и том, на который указывает большинство законов и контрактов. Если ваш продукт ведёт живое аудио с видео, уровень AA означает живые субтитры, и точка.
В дедлайн эту рекомендацию превратило правило Министерства юстиции США 2024 года в рамках Americans with Disabilities Act (ADA). Оно требует от органов власти штатов и местного уровня соответствия WCAG 2.1 уровня AA для веб-контента и мобильных приложений и несёт жёсткие даты: учреждения, обслуживающие более пятидесяти тысяч человек, должны соответствовать к 24 апреля 2026 года, а меньшие – к 26 апреля 2027 года. Любая платформа, продающая государственным университетам, судам, городским администрациям или государственным школам, наследует этот дедлайн через своих клиентов. Для телевидения США и части интернет-видео Federal Communications Commission (FCC) требует субтитрования уже много лет; правило DOJ переносит давление прямо в мир веб-конференций и вебинаров.
Вывод – не юридическая консультация (берите своего юриста), а факт для планирования: если среди ваших покупателей есть госучреждения или крупные предприятия, у «субтитров», скорее всего, есть дата соответствия в 2026 году, и выбранная архитектура должна быть готова до неё.
SFU, Который У Вас Уже Есть
Теперь к архитектуре – с детали, которую вы почти наверняка уже эксплуатируете. В звонке с более чем двумя-тремя участниками звук и видео не летят напрямую между всеми; это заставило бы каждое устройство отправлять отдельную копию каждому другому, что разваливается на телефонах и слабых каналах. Вместо этого потоки проходят через сервер посередине – Selective Forwarding Unit, или SFU. Каждый участник отправляет одну копию своего звука и видео наверх в SFU, а SFU пересылает – «раздаёт веером», fan-out – нужные потоки вниз всем остальным.
Представьте общую встречу на 30 человек. Без SFU телефон говорящего загружал бы 29 копий своего звука. С SFU телефон загружает одну копию, а сервер рассылает её 29 слушателям. Этот fan-out – один поток внутрь, много потоков наружу – это и есть вся работа SFU, и именно благодаря ему групповые звонки вообще работают. О более широкой роли SFU и браузерных хуках вокруг него мы пишем в статье про интеграцию ИИ и WebRTC.
Вот инсайт, на котором держится вся остальная статья: SFU – единственное место в системе, которое уже видит звук каждого участника, разделённый по говорящим, в реальном времени. Это делает его естественным местом для подключения распознавания речи. Вы не добавляете новую трубу; вы подключаетесь к трубе, которая уже несёт ровно то, что нужно движку ASR.
Два Места Для Уха: На Клиенте Или На Сервере
Распознавание речи можно запустить ровно в двух местах, и выбор между ними – это и есть настоящее решение. Разницу проще всего увидеть, если посчитать.
Первый вариант – на клиенте: устройство каждого участника распознаёт звук, который слышит. Это кажется естественным – звук и так приходит на каждое устройство, чтобы воспроизвестись, – и держит голоса вне ваших серверов, что звучит приватно. Но посчитайте стоимость. На встрече в 30 человек, если каждое устройство распознаёт звонок, вы гоняете тридцать отдельных задач распознавания речи для одного разговора. Каждое устройство жжёт батарею и процессорное время; дешёвый телефон может вообще не справиться и начнёт ронять кадры или перегреваться. Хуже того, тридцать устройств выдадут тридцать слегка разных расшифровок, потому что каждое прогнало свой движок по своему чуть-чуть отличающемуся принятому звуку – так что два человека на одной встрече видят разные субтитры, а ваша запись, если вы её храните, не совпадает ни с одной.
Второй вариант – на сервере: SFU один раз снимает звук каждого говорящего, распознаёт его централизованно и рассылает получившийся текст всем. Снова посчитайте. Той же встрече на 30 человек распознавание нужно только для тех, кто реально говорит, – обычно для одного, иногда для двоих. Вы гоняете одну-две задачи распознавания вместо тридцати. Каждый участник видит ровно один и тот же субтитр, потому что есть единственный источник истины. Текст рассылать почти ничего не стоит. И это работает одинаково на флагманском ноутбуке и на трёхлетнем телефоне, потому что телефону нужно лишь показать текст, а не сгенерировать его.
В такой формулировке выбор выглядит однобоким, и для групповых звонков он такой и есть. У распознавания на клиенте есть своя ниша – звонок один на один, офлайн- или приватный продукт, где звук не должен покидать устройство, или резервный путь, когда серверный путь упал, – и этот путь мы разбираем в отдельной статье про клиентский ASR с faster-whisper в браузере. Но для любой встречи с толпой распознавание на сервере – это паттерн, который масштабируется, и живёт он на SFU.
Паттерн Fan-Out, Шаг За Шагом
Сложите детали – и получите паттерн, в честь которого названа статья. Он переиспользует уже существующую работу SFU – раздачу медиа – и добавляет вторую, куда меньшую раздачу: текста.
Пройдём путь одной фразы. Maria говорит. Её устройство отправляет один аудиопоток наверх в SFU – ровно как уже делает, чтобы остальные её слышали. Серверный процесс подписывается на этот поток – он просто ещё один потребитель звука, который SFU и так пересылает, – и направляет звук Марии в потоковый ASR-движок. Движок возвращает текст. Этот текст упаковывается в субтитровые cue и отдаётся обратно в SFU, который раздаёт его всем 30 участникам по боковому каналу, предназначенному для данных, а не для медиа. Приложение каждого участника принимает cue и рисует его на экране под видео Марии.
Итак, два fan-out на одном сервере: звук раздаётся, чтобы люди слышали, а текст субтитров раздаётся, чтобы люди читали. Раздача текста почти бесплатна по сравнению с раздачей звука – строка субтитра это несколько десятков байт, тогда как секунда звука это тысячи, – поэтому добавить субтитры к звонку, который уже идёт через SFU, это в основном вопрос проводки, а не новой инфраструктуры.
Одно уточнение делает паттерн эффективным, а не расточительным, и это шарнир всей конструкции: вы не распознаёте каждый аудиопоток всё время. Вы распознаёте только потоки, в которых сейчас есть речь. Маленький дешёвый детектор под названием Voice Activity Detection – VAD, программа, отвечающая на бинарный вопрос «говорит ли кто-нибудь в этом звуке прямо сейчас?» – стоит на воротах перед дорогим ASR. Когда Maria говорит, её поток получает задачу распознавания; когда она замолкает, задача останавливается. Поскольку на реальной встрече одновременно говорят один-два человека, VAD-гейтинг означает, что вы платите за одну-две задачи распознавания независимо от того, сколько людей в комнате.
Считаем Стоимость Вслух
Причина, по которой паттерн важен коммерчески, – число, которое можно посчитать в одну строку, так что посчитаем. Потоковое распознавание речи тарифицируется по минутам обработанного звука. Показательная ставка 2026 года – потоковый тариф Deepgram Nova-3, около $0,0077 за минуту, то есть примерно $0,46 за час звука.
Возьмём часовую встречу на 30 человек. Наивный подход – распознавать дорожку каждого участника всё время – гоняет 30 потоков по 60 минут:
наивная стоимость = 30 потоков × 60 мин × $0,0077/мин
наивная стоимость = 1 800 поток-минут × $0,0077
наивная стоимость = $13,86 за одну встречуТеперь паттерн fan-out с VAD-гейтингом. На часовой встрече предположим в среднем одного активного говорящего за раз, иногда двух – назовём это 1,5 потока реальной речи:
стоимость fan-out = 1,5 потока × 60 мин × $0,0077/мин
стоимость fan-out = 90 поток-минут × $0,0077
стоимость fan-out = $0,69 за одну встречуВот и весь аргумент в двух суммах: $13,86 против $0,69, разница в двадцать раз, за субтитры, которые к тому же более согласованны и работают на слабых устройствах. Масштабируйте до продукта с десятью тысячами таких встреч в месяц – и разрыв это примерно $138 600 против $6 900: разница между функцией, которая тихо течёт деньгами, и той, что едва заметна в счёте. Рычаг – это VAD-гейтинг, и забыть про него – самая дорогая ошибка во всей теме.
Черновик Против Финала: Субтитр, Который Сам Себя Переписывает
У живых субтитров есть поведение, которое сбивает с толку при первом знакомстве, и понимание его предотвращает целый класс багов. Присмотритесь к живому субтитру – и вы увидите, как слова появляются, потом меняются, потом застывают. «Думаю, нам стоит» становится «Думаю, нам стоит выпустить» становится «Думаю, нам стоит выпустить это в пятницу». Субтитр переписывает сам себя на месте. Это не глюк; так работает потоковое распознавание, и ваш дизайн должен это ожидать.
Потоковые ASR-движки выдают два рода результатов. Первый – черновик (partial), он же промежуточный (interim) результат: быстрая грубая догадка о сказанном, отправленная пока человек ещё говорит. Он помечен как не финальный и, скорее всего, изменится по мере поступления нового звука и переосмысления движком. Второй – финальный результат, отправляемый, когда движок уверен, что фраза завершена и текст больше не изменится. Завершённость фразы движок определяет через endpointing – обнаружение короткой паузы или спада речи, отмечающего конец высказывания, та же идея VAD, применённая для поиска границ, а не просто наличия.
Компромисс – отзывчивость против стабильности. Показывайте только финалы – и субтитры ощущаются тормозными, появляясь через секунду-две после конца фразы. Показывайте черновики – и субтитры ощущаются мгновенными, но мерцают, исправляя себя. Продакшен-системы показывают черновики, чтобы субтитр ощущался живым, а затем фиксируют каждую строку, когда приходит её финал: только что прочитанные слова перестают двигаться, а ниже начинается следующая грубая догадка. Поэтому ваш клиентский код должен трактовать входящий субтитр как замени текущую незавершённую строку, а не добавь новую строку, пока не придёт финал. Команды, добавляющие каждый черновик, получают субтитры, которые заикаются и повторяются; команды, заменяющие до финала, получают то самое чистое застывание, которого ждут пользователи. Глубже про механику черновик-против-финала – в статье про потоковый ASR.
Доставка Текста: Data Channel
Субтитры сгенерированы; теперь они должны дойти до 30 экранов. Они не едут в звуке или видео – это непрерывные медиапотоки, где нет места произвольному тексту. Они едут по отдельному пути, встроенному в WebRTC ровно для такой нагрузки: по data channel.
Data channel – это стандартизированный способ для браузеров в сессии WebRTC отправлять друг другу произвольные данные, текст или байты, определённый в спецификации IETF RFC 8831 «WebRTC Data Channels». Стоит знать одно проектное решение, которое он предлагает, потому что оно аккуратно ложится на субтитры. Канал можно настроить как надёжный и упорядоченный (reliable and ordered) – каждое сообщение гарантированно дойдёт и придёт в той же последовательности, что и было отправлено, – или как частично надёжный и неупорядоченный (partially reliable and unordered) – система может терять или переставлять сообщения в пользу скорости. Лежащий в основе транспорт, SCTP, делает надёжность и упорядоченность свойством каждого сообщения, а не всего соединения, так что продукт может даже смешивать оба режима.
Для субтитров обычный выбор – надёжный и упорядоченный: пропущенная или переставленная строка субтитра раздражает, а объём текста настолько мал, что гарантия доставки почти ничего не стоит. Единственный повод ослабить это – черновики: поскольку каждый черновик через мгновение будет заменён следующим черновиком или финалом, потерять один на лету безвредно, поэтому некоторые системы отправляют черновики «как получится», а финалы – надёжно. В любом случае субтитровый cue – текст, подпись говорящего и тайминг, те же поля, что несёт cue WebVTT, – сериализуется, отправляется один раз из SFU в data channel и раздаётся каждому участнику, который его десериализует и рисует строку.
Здесь же фреймворки для конференций прячут проводку. LiveKit, например, выставляет транскрипции как поток первого класса, который его сервер публикует клиентам, а клиентские SDK отдают как готовые к отрисовке события текста, так что код приложения подписывается на субтитры, а не разбирает data channel руками. Open-source-стэки делают то же на свой лад: серверный транскрайбер Jitsi, Jigasi, отправляет результаты распознавания обратно во встречу как JSON, который клиент Jitsi Meet превращает в экранные субтитры. Транспорт под капотом – data channel; фреймворк просто даёт ему более дружелюбное имя.
Бюджет Задержки Для Субтитра
Пользователи прощают субтитрам отставание от речи на мгновение; они не прощают отставание на много секунд. Поэтому полезно сложить, откуда берётся задержка субтитра, ведь итог – это бюджет, под который вы проектируете, та же дисциплина, что мы применяем ко всему звонку в статье про бюджет задержки до 100 мс, здесь применённая к тексту.
У путешествия субтитра четыре участка. Первый – звук говорящего едет наверх в SFU: десятки миллисекунд на приличном канале. Второй – ASR-движок слушает и выдаёт черновик; это самый большой участок, обычно несколько сотен миллисекунд у быстрого потокового движка на первую догадку. Третий – текст субтитра едет из SFU вниз к участникам по data channel: снова десятки миллисекунд, потому что текст крошечный. Четвёртый – клиент его рисует. Сложим участки:
задержка субтитра ≈ 80 мс (звук вверх) + 300 мс (черновик ASR) + 60 мс (текст вниз) + отрисовка
задержка субтитра ≈ менее полусекунды до первой черновой догадкиИтак, хорошо построенная функция субтитров показывает грубую строку примерно через полсекунды после того, как кто-то заговорил, а затем застывает её в финал через секунду-две, по мере завершения фразы. Доминирующий член – ASR-движок, поэтому при выборе вендора сравнивать нужно задержку до первого черновика движка, а не сеть. Всё, что добавляет сверху секунду – например, прогон звука через лишний шаг микширования перед распознаванием или ожидание финалов перед показом, – пользователи ощущают сразу, и это стоит оспаривать на ревью дизайна.
На Клиенте Против На Сервере: Таблица Решения
Два подхода аккуратно выстраиваются по критериям, которые важны при описании функции.
| Критерий | ASR на клиенте (каждое устройство) | Fan-out ASR на сервере (на SFU) |
|---|---|---|
| Задач распознавания на звонок из 30 человек | До 30 (по одной на устройство) | 1–2 (только активные говорящие, VAD-гейт) |
| Согласованность субтитров | У каждого устройства своя | Единый источник истины, одинаково у всех |
| Работает на дешёвом телефоне | Часто нет – садит батарею, может упасть | Да – телефон лишь показывает текст |
| Стоимость облака | Нет (работает на устройствах) | Низкая, ограничена говорящими, не слушателями |
| Звук покидает устройство | Нет – сохраняет приватность | Да – звук доходит до вашего сервера |
| Запись / поиск / перевод | Сложно – нет центральной расшифровки | Просто – центральная расшифровка уже есть |
| Лучше всего подходит | Звонки 1:1, офлайн / приватные, резерв | Групповые звонки, вебинары, всё с записью |
Читайте таблицу по своему продукту. Групповой вебинар или класс, который вы ещё и записываете и хотите переводить, – это однозначно серверный путь: вы получаете одну расшифровку, которая разом и субтитрует, и архивирует, и кормит перевод. Приватный продукт один на один, где звук не должен касаться сервера, – это клиентский путь, с принятием более высокой стоимости на устройстве. Большинство конференц-продуктов – первый случай, поэтому паттерн fan-out и стоит по умолчанию.
Частая Ошибка: Распознавать Каждую Дорожку Всё Время
Самая дорогая ошибка в этой области – та, на которую уже намекнула математика стоимости: гонять распознавание речи на аудиопотоке каждого участника непрерывно, вместо только тех потоков, где сейчас есть речь. Ошибиться легко, потому что это самое простое в реализации – подпишись на все дорожки, направь их все в ASR-движок, готово, – и оно отлично работает в тесте на двоих, где оба и есть активные говорящие.
Потом оно выезжает на общую встречу в 30 человек, и счёт оказывается в двадцать раз больше, чем должен, как показала математика выше, без всякой пользы – 28 молчащих участников генерируют пустые расшифровки по полной цене. Лечится это не более быстрым движком и не более дешёвым вендором, а VAD-гейтом. Сначала обнаружь речь, потом распознавай. Второй, родственный промах – оставлять распознавание работать сквозь долгие паузы на одном потоке; тот же гейт его решает. Относитесь к вопросу «говорит ли кто-нибудь на этом потоке прямо сейчас» как к тому, на который нужно ответить дёшево прежде, чем тратить деньги на ответ «что они говорят», – и проблема стоимости исчезает.
Строить На Фреймворке, Покупать API Или И То И То
Когда паттерн определён, практический выбор – из чего вы его собираете. У трёх слоёв своё решение «строить или покупать», и они независимы.
Первый – медиасервер. Можно поднять open-source SFU – mediasoup, Janus, Jitsi – и владеть аудио-съёмом самому, или взять хостинговую real-time-платформу вроде LiveKit, которая даёт SFU плюс встроенный поток транскрипции, так что субтитры ближе к настройке, чем к проекту. Self-hosting меняет инженерное время на контроль и более низкую поминутную стоимость; хостинговый путь меняет плату на скорость запуска.
Второй – движок распознавания, и тут вы почти всегда покупаете API. Вендоры потокового ASR различаются по осям, важным для живых субтитров: задержка до первого черновика, точность на шумном реальном звуке, охват языков и цена. Deepgram лидирует по низкой задержке и товарной цене, AssemblyAI и Speechmatics сильны по точности на «грязном» звуке, а self-hosted Whisper или речевой стек NVIDIA – путь, когда звук не может покидать вашу инфраструктуру или поминутная плата должна стремиться к нулю на масштабе. Подробно мы сравниваем их в статье про потоковый ASR; конференц-паттерн из этой статьи одинаков, какой бы движок вы ни выбрали.
Третий – всё вокруг сырой расшифровки: разметка, кто говорил, то есть диаризация диктора, когда говорящие не разделены по дорожкам; предварительная очистка звука шумоподавлением, чтобы движок слышал слова, а не стук клавиатуры; и, если вы обслуживаете несколько языков, подача той же расшифровки в перевод речи в реальном времени. Паттерн fan-out – это хребет; это мышцы, которые вы к нему добавляете.
Где Здесь Фора Софт
Мы строим живые видеопродукты, где субтитры теперь – обязательная база: платформы видеоконференций, виртуальные классы и e-learning, телемед-консультации, инструменты прямых трансляций и вебинаров – и строим их на паттерне fan-out на стороне SFU, описанном в этой статье, потому что это та версия, что переживает встречу с реальным счётом клиента. Дисциплина, которую мы применяем, – та, что отстаивается здесь: распознавать на медиасервере, который вы и так держите, ставить распознавание за ворота детектора речевой активности, чтобы стоимость шла за говорящими, а не за местами, трактовать черновики как незавершённые строки, фиксируемые на финале, и доставлять текст по надёжному data channel, чтобы каждый участник читал один и тот же субтитр. Поскольку мы работаем в образовании и здравоохранении, мы планируем дедлайн доступности с самого начала, а не прикручиваем субтитры потом, и держим центральную расшифровку готовой кормить запись, поиск и перевод, ведь клиент, попросивший субтитры в этом квартале, обычно просит их в следующем.
Ключевые Выводы
- Живой субтитр – это текст с таймингом и подписью говорящего, созданный ASR и показанный за секунду-две.
- Распознавайте один раз на говорящего на SFU и раздавайте текст веером, а не на каждом устройстве.
- VAD-гейт держит стоимость – вы платите за активных говорящих, а не за места.
- Субтитры приходят быстрыми черновиками, которые сами себя переписывают, потом фиксируются в финал.
- Текст субтитров едет по data channel WebRTC (RFC 8831), обычно надёжно и упорядоченно.
- Живые субтитры – требование уровня AA в WCAG, обязательное для крупных госорганов США с апреля 2026.