Веб-камера в потоке: что видит другая сторона
Изображение, которое вы видите в предварительном просмотре веб-камеры, — это локальный захватный вид — кадр камеры, доставляемый через getUserMedia(), — а не сырой выход датчика. Изображение, которое видит участник вашего видеозвонка, — это браузерный закодированный поток, который был молча согласован, сжат и, возможно, понижен качеством WebRTC. Это руководство объясняет, как работает согласование кодеков WebRTC, как использовать getStats() на RTCPeerConnection для измерения фактических параметров потока и как исправить распространённые причины потери качества. Обратите внимание: инструмент веб-камеры этого сайта проверяет только локальный захватный трек и частоту кадров браузера; он не измеряет исходящий WebRTC-кодируемый поток.
Вы сидите в видеозвонке, и ваше собственное окно показывает чёткое, хорошо освещённое изображение 1080p. Вы выглядите резко, фон чистый, и кадрирование правильное. Затем коллега говорит, что ваше видео постоянно замирает и выглядит размытым, а когда он демонстрирует экран, вы видите себя в виде блочного, с размытыми краями изображения, которое могла бы произвести бюджетная веб-камера десятилетней давности. Та же камера. Тот же свет. Тот же компьютер. Другое изображение.
Вот что пропускают большинство руководств по видеозвонкам: этот разрыв обычно не аппаратная неисправность и не дефект датчика — хотя сбойный драйвер иногда способен это вызвать. Обычно это разница между тем, что производит ваша камера, и тем, что на самом деле отправляет ваш браузер. Ваш превью берётся из локального захватного пути — кадра камеры, доставляемого через getUserMedia(), — до кодирования WebRTC, что делает его более выгодным видом вашего видео, чем то, что получают другие участники. Этот вид может всё же включать масштабирование, цветовое преобразование или обработку ограничений, применяемые браузером, поэтому это не сырой выход датчика. То, что получают другие участники, — это сжатый поток, который был согласован, перекодирован, ограничен по скорости и, возможно, понижен качеством WebRTC в миллисекундах перед тем, как покинуть ваш компьютер.
Этот слой кодирования невидим из пользовательского интерфейса и молчалив при нормальной работе. Ничто в окне звонка не говорит, что он существует, ничто не сообщает его решений, и ничто в превью не отражает их. Знакомая сцена: пользователь тратит часы на переустановку драйвера камеры, потому что звонок показывает размытое изображение — только чтобы обнаружить через chrome://webrtc-internals, что браузер согласовывал 480p15 на основе оценки пропускной способности, а не из-за камеры или драйвера. На этой странице объясняется, что происходит между датчиком камеры и экраном вашего собеседника, почему браузер молча снижает качество — и, что важнее всего, — как измерять фактический поток, покидающий ваш компьютер, вместо того чтобы доверять превью.

Что ваш превью вам не показывает
Видеозвонки опираются на конвейер, который работает совершенно вне вашего сознания. Каждый кадр, который вы отправляете, проходит через следующие этапы:
- Датчик камеры захватывает кадр; затем браузер доставляет его через getUserMedia() с запрошенным разрешением и частотой кадров, что может включать масштабирование, цветовое преобразование и обработку ограничений.
- Браузер получает этот кадр через API getUserMedia() и передаёт его движку WebRTC. Ваш локальный превью берётся из этого этапа — поэтому он всегда выглядит идеально, независимо от того, что происходит дальше.
- Кодировщик WebRTC сжимает кадр в битовый поток с использованием согласованного кодека, согласованного разрешения, частоты кадров и битрейта.
- Закодированный кадр разбивается на RTP-пакеты и отправляется по сети, обычно через медиа-сервер, который пересылает его всем остальным.
- Каждый приёмщик декодирует пакеты обратно в изображение и отображает его на экране.
Ваш превью подключается к конвейеру на втором этапе. Он показывает кадр камеры, доставляемый через getUserMedia(), до кодирования WebRTC. Браузер может применить масштабирование, цветовое преобразование или обработку ограничений на этом этапе, но никакого сжатия WebRTC не применяется. Поток, который видит ваш собеседник, — это то, что выходит из третьего этапа. Между этими двумя точками кодек решает, что получит мир, и он принимает это решение на основе условий, которые вы никогда не видите.
Кодек молча согласовывает три параметра, и каждый может измениться во время звонка без какого-либо уведомления:
- Разрешение — ширина и высота закодированных кадров, которые могут пасть с 1920x1080 до 1280x720 или 640x480 самостоятельно.
- Частота кадров — сколько кадров в секунду выживает кодирование, которая обычно падает первой (с 30 до 15 или ниже), поскольку уменьшение частоты кадров вдвое сокращает скорость передачи данных вдвое без изменения разрешения — хотя некоторые реализации могут вместо этого снизить разрешение в зависимости от кодека и платформы.
- Битрейт — сколько битов в секунду кодеку разрешено тратить, что задаёт общий уровень сжатия.
Эти три значения определяют всё, что видит другая сторона. Изображение 1080p, сжатое до низкого битрейта, выглядит мягким и смазанным. Поток 30 кадров в секунду, внезапно работающий на 10 кадрах, выглядит дерганым и неестественным. Когда вы понимаете, что превью не сообщает ни одно из этих значений, загадка ужасного звонка с отличной камерой разрешается.
SDP согласует раньше, чем вы пошлёте кадр
Согласование начинается до того, как отправлен первый кадр. Когда вы присоединяетесь к звонку, платформа и ваш браузер обмениваются предложением и ответом Session Description Protocol (SDP). SDP рекламирует кодеки и мультимедийные возможности, которые поддерживает каждая сторона; фактическое разрешение и частота кадров далее согласовываются через параметры отправки и приёма и ограничиваются оценкой пропускной способности. В неформальном тестировании на Chrome 125 на Windows 10 (май 2024) ответ SDP наблюдался предлагать VP9 первым, затем H.264, затем VP8; на Safari 17 на macOS 14 (тот же период) H.264 появлялся первым. Эти наблюдения были сделаны на одном компьютере для каждого браузера без контролируемых сетевых условий, поэтому они иллюстрируют различия, а не определяют универсальный порядок. Фактический выбранный кодек зависит от браузера, платформы, параметров SDP и аппаратных возможностей. Обычные выборы включают VP9, H.264, VP8 и AV1, но единого фиксированного порядка предпочтений не существует.
Выбор кодека — только начало. Как только звонок жив, вступает в силу второй механизм: оценка пропускной способности. WebRTC непрерывно мониторит сетевой путь в обоих направлениях и запускает алгоритм управления перегрузкой — чаще всего Google Congestion Control — который делает три измерения на скользящей основе:
- Потеря пакетов, сообщаемая приёмщиком через сообщения обратной связи RTCP.
- Круговое время, задержка между отправкой пакета и получением подтверждения его прибытия.
- Пропускная способность, эффективная скорость, с которой данные действительно проходят по соединению.
Алгоритм подаёт эти измерения в модель, которая оценивает, сколько пропускной способности сеть способна принять сейчас. Эта оценка становится целевым битрейт, передаваемым кодеку, и кодек адаптирует свой вывод. Петля непрерывна и быстра: когда оценка падает, кодек перекодирует при более низком битрейте в доли секунды.
Пороги приблизительны и зависят от конкретного используемого алгоритма управления перегрузкой. Устойчивая потеря пакетов выше примерно пяти процентов может вызвать снижение целевого битрейта, но точное поведение — насколько падает скорость, меняется ли разрешение или частота кадров и при каком уровне потерь — варьируется от алгоритма к алгоритму, кодеку и платформе. Универсального правила из процента потерь к конкретному разрешению вывода не существует. Обратная связь, которая управляет этими решениями, определена в белых бумагах WebRTC и RFC 8888. Поток 1080p при 30 кадрах в секунду обычно требует около четырёх-шести мегабит в секунду пропускной способности загрузки, чтобы выглядеть чисто, хотя это зависит от кодека, настроек кодирования и сложности сцены. Если оценщик делает вывод, что доступен только один мегабит, он не пытается протолкнуть поток — он снижает цель, и кодек отвечает снижением разрешения до 480p и частоты кадров до 15 кадров в секунду. Если оценка падает ещё, изображение деградирует снова, вплоть до говорящей головы 320x240. Каждый шаг намерен: цель — удержать звонок живым и аудио стабильным, а качество видео — первое, что жертвуют ради этого (Google: WebRTC Congestion Control Whitepaper, 2019; RFC 8888: Congestion Control Feedback for RTP).

Три узора отказа, которые скрывает поток
Превью скрывает фактическое состояние потока за тремя повторяющимися узорами отказа. Научитесь распознавать каждый по симптомам, потому что правильное лечение различается для каждого случая. Все три делят один признак: превью остаётся идеальным всё время, потому что берётся до кодека. Только закодированный поток — и поэтому экраны других участников — показывает урон.
Ловушка битрейта
Симптомы: ваш превью идеален, и процессор вашего компьютера свободен. Другая сторона видит размытое, низкоразрешенное изображение, которое никогда не чётится, даже в паузе. Локальные записи выглядят отлично; звонок — плохо. Диагноз: браузер оценил, что сеть не способна нести полный поток, и ограничил битрейт, заставляя кодек сжимать разрешение. Ограничитель — ваша пропускная способность загрузки или способность ретрансляции медиа-сервера, а не ваша камера. Запустите тест скорости и сравните вашу загрузку с четырьмя-шестью мегабитами, которые требует 1080p30. Если вы делите соединение с другими устройствами, стримящими, скачивающими или синхронизирующими, они конкурируют за один и тот же бюджет.
Порог потерь
Симптомы: статические кадры из звонка выглядят хорошо, но любое движение дерганое и роботизированное. Аудио остаётся плавным, пока видео задёргивается, и короткие заморозки происходят каждые несколько секунд, после чего изображение возобновляется на более низкой частоте кадров. Диагноз: потери пакетов периодически пересекают пятипроцентный порог, поэтому контроллер перегрузки неоднократно режет целевой битрейт, и кодек сбрасывает кадры для выравнивания. Частота кадров падает первой, потому что это самый дешёвый рычаг — снижение с 30 до 15 кадров в секунду сокращает скорость передачи вдвое без изменения разрешения. Проверьте Wi-Fi-помехи, пограничный Ethernet-кабель или VPN, добавляющий потери и задержки. Видео будет продолжать деградировать, пока сетевой путь не будет отремонтирован.
Заморозка ключевого кадра
Симптомы: первые две-пять секунд после присоединения или демонстрации экрана другая сторона видит замороженное или сильно смазанное изображение. Затем изображение вступает в фокус и ведёт себя нормально до следующего крупного изменения сцены. Диагноз: видеокодеки отправляют два типа кадров. Ключевые кадры кодируют целое изображение и появляются с фиксированным интервалом — обычно каждые 1–10 секунд в зависимости от платформы и конфигурации. Дельта-кадры кодируют только изменения относительно предыдущего кадра, что делает их крошечными. Если пакет дельта-кадра потерян, приёмщик не может восстановить этот кадр — он может запросить повторную передачу (NACK), попросить отправителя новый ключевой кадр (PLI или FIR) или скрыть ошибку в ожидании. Когда запрос успешен, восстановление быстрое; когда нет — изображение остаётся замороженным до прихода следующего запланированного ключевого кадра. Это ожидание — заморозка, которую вы видите. Короткие интервалы ключевых кадров восстанавливаются быстрее, но стоят пропускной способности; длинные интервалы эффективны, но делают каждую потерю более видимой. Это не сбой — это кодек, работающий по замыслу.

Измерение фактического потока через getStats()
Перестаньте угадывать. Цифры не льстивы, но они правдивы. API WebRTC открывает интерфейс статистики, который сообщает ровно то, что делает кодек, и любая веб-страница, которую вы контролируете, может читать свою собственную peer-соединение. Вызовите getStats() на объекте RTCPeerConnection, затем отфильтруйте возвращённый отчёт для записи outbound-rtp с kind равным video. На записях media-source и outbound-rtp вы найдёте значения, которые имеют значение:
- framesPerSecond (на записи media-source) — частота кадров, которую камера доставляет кодеку. Если она ниже 30 на камере 30 кадров в секунду, сам захватный путь — проблема — часто причина в конкуренции за полосу USB; как её диагностировать, объясняет наш справочник о конкуренции за полосу USB.
- framesPerSecond (на записи outbound-rtp) — частота кадров, которую кодек фактически производит. Когда она ниже входной частоты, кодек сбрасывает кадры.
- frameWidth и frameHeight (на записи outbound-rtp) — закодированное разрешение. Сравните с настройками звонка; любое различие — молчаливое понижение.
- bytesSent (на записи outbound-rtp) — общий закодированный объём. Возьмите два измерения с интервалом в секунду и вычислите разницу, умноженную на 8, чтобы получить фактический битрейт.
- packetsSent и packetsLost — обратите внимание: packetsLost на записи outbound-rtp не всегда заполнена; коэффициент потерь надёжнее читать из записи remote-inbound-rtp, которая сообщает перспективу приёмщика.
- currentRoundTripTime (на записи candidate-pair) — сетевая задержка. Высокое круговое время в сочетании с потерями указывает на перегрузку.
Разные браузеры могут сообщать слегка разные имена полей или опускать некоторые статистики, поэтому перепроверяйте по странице WebRTC-внутренностей браузера, когда она доступна.
Правила интерпретации просты. Если outbound-rtp framesPerSecond заметно ниже media-source framesPerSecond, кодек сбрасывает кадры под давлением. Если frameWidth и frameHeight ниже настроенного разрешения, бюджет соединения не смог нести его. Если bytesSent переводится в битрейт, значительно ниже требования кодека для этого разрешения, изображение сжимается — а превью, которое никогда не видит кодека, будет выглядеть отлично всё время.
Вам не нужно писать код, чтобы читать эти значения. Chrome и Edge поставляются со встроенным просмотрщиком статистики на chrome://webrtc-internals, который фиксирует каждое WebRTC-соединение в браузере, включая то, которое создала платформа звонков. Откройте его до присоединения к встрече, позвольте ему записывать, затем проверьте записи outbound-rtp для видеосендера. Те же цифры частоты кадров, разрешения, битрейта и потерь там, а также график таймлайна, показывающий, когда именно кодек изменил вывод. Это самый быстрый способ поймать понижение качества в акте в реальном звонке платформы.
Выполните это измерение, пока ваш звонок простаивает в фоне, затем снова во время загруженной встречи и сравните два набора цифр. Типичное сравнение: в простое outbound-rtp framesPerSecond показывает 30; во время загруженной встречи он может упасть до 18, в то время как локальный превью по-прежнему показывает 30. Это сравнение выявляет, адекватна ли ваша базовая сеть и является ли конкуренция во время звонка тем, что вызывает понижение. Наш тест веб-камеры показывает разрешение и частоту кадров локального захватного трека, согласованного через getUserMedia() — полезный базис для того, что способна доставить камера в браузер, но не измерение исходящего WebRTC-кодируемого потока, для которого требуется peer-соединение или страница внутренних WebRTC-настроек браузера.
Zoom, Meet, Teams: три разные камеры
Одна и та же камера, один и тот же ноутбук и одна и та же сеть могут давать визуально различные результаты в разных приложениях, потому что каждая платформа применяет свою собственную политику кодирования поверх реализации WebRTC браузера.
- Zoom обычно отдаёт приоритет стабильности. В зависимости от версии, типа аккаунта и настроек встречи он может ограничить исходящее разрешение и пожертвовать качеством видео, чтобы сохранить стабильность аудио и демонстрации экрана. Камера, способная на 1080p, может транслировать 720p или ниже в Zoom.
- Google Meet обычно адаптируется к измеренной пропускной способности, рано снижая разрешение и быстро восстанавливая его при улучшении условий. Его поведение отслеживает фактическое сетевое состояние, на пользу и во вред.
- Microsoft Teams применяет свою собственную цепочку кодирования и пост-обработки. В зависимости от версии и настроек встречи изображение может сжиматься дополнительно за пределами сырого согласования WebRTC.
- Нативный браузерный WebRTC, который вы получаете на голой веб-странице, отправляет согласованный поток без политики платформы поверх. Это ближайшее к истине, на что способны ваше оборудование и сеть в чистом WebRTC. Политики платформ различаются по версии, типу аккаунта и конфигурации встречи, поэтому воспринимайте это как общие тенденции, а не жёсткие правила.
Практическое следствие: сравнение звонка в Zoom со звонком в Meet — это не тест камеры. Это тест решений каждой платформы о кодировании. Сначала измерьте сырой поток простой страницей WebRTC или инструментом тест веб-камеры. Если сырой поток чист, а одна платформа всё ещё выглядит плохо, политика платформы — ограничитель, и никакое обновление оборудования не изменит её. Во время звонка платформы chrome://webrtc-internals позволяет наблюдать решения её кодека в реальном времени, так что вы видите, понижение — политика платформы или поведение сети — не доверяя ни одной из сторон.
Семь шагов к чистому потоку
Эти шаги изолируют различные причины. Вы можете остановиться, как только поток выглядит правильно — но измеряйте до и после каждого изменения, чтобы подтвердить, что ремонт действительно двигал цифры.
- Сначала измерьте. Откройте chrome://webrtc-internals или используйте getStats() на своём peer-соединении и запишите разрешение, частоты кадров и битрейт. Если сырой поток уже понижен, камера и соединение — проблема. Если чистый, то платформа.
- Проверьте пропускную способность. Видеозвонкам нужен минимум 1 Мбит/с загрузки для 720p и около 4 Мбит/с для 1080p. Тестируйте с компьютера, с которого звоните, а не с другого устройства, и перепроверяйте при наличии другого трафика в сети. Перейдите на проводное соединение, если можете; Wi-Fi-потери — самая частая причина невидимого понижения.
- Исправьте освещение. Хорошо освещённый субъект сжимается значительно лучше, чем тёмный, потому что кодек тратит битрейт на видимые детали, а не на шум. Обернитесь к окну или используйте мягкий источник света. Это ничего не стоит и часто даёт больше улучшения, чем любая настройка.
- Отключите видеошумоподавление, когда ваша среда уже тихая и хорошо освещена. Некоторые платформы применяют агрессивное денормирование в условиях низкой освещённости, что может заметно сглаживать мелкие детали в закодированном потоке. Локальный превью не покажет эту разницу; только закодированный поток. Проверьте настройки конкретной платформы, чтобы уточнить доступные элементы управления.
- Отключите виртуальный фон. Сегментация фона добавляет накладные расходы к фактическому битрейте и потребляет GPU-такты у кодека. Чистый реальный фон с постоянным освещением побеждает любой виртуальный.
- Проверьте кодек. Если ваш процессор справляется, VP9 даёт лучшее качество на бит; если вы видите потерю кадров или скачки процессора, H.264 кодирует быстрее. Некоторые платформы предоставляют предпочтение кодека в настройках.
- Тестируйте другую платформу. Если качество хорошее на одном сервисе и плохое на другом, политика кодирования платформы — ограничитель. Адаптируйте ожидания или инструмент встречи соответственно — оборудование не виновато.
Итог
Превью — это локальный самовид вашей камеры: выгодный вид того, что способна доставить камера в браузер. Поток — это то, что видит мир на самом деле — согласованный, сжатый и сформированный сетевыми условиями и политикой платформы. Это два разных видео одного субъекта, и только одно из них важно для того, как вы выглядите. Если вы когда-нибудь задавались вопросом, почему камера отлично выглядит на вашем экране, но ужасно в звонке, у вас теперь есть ответ и инструмент для измерения.
Откройте chrome://webrtc-internals. Присоединитесь к следующему звонку. Наблюдайте за записями outbound-rtp. Если frameWidth или framesPerSecond ниже того, что вы задали в настройках камеры, понижение происходит. Причина обычно связана с пропускной способностью — что разрешено тратить вашему браузеру и способна ли ваше соединение фактически нести поток, который, как вам кажется, вы отправляете, — но загрузка процессора, выбор кодека, политики платформы и конфигурация ограничений также могут способствовать деградации или доминировать над ней.