Real-Time Backend

Media Streams

Google Meet запускается прямо в браузере: нажал ссылку - и ты уже в звонке с видео и звуком. Никаких установок. Как браузер получает доступ к камере и микрофону, и как это видео попадает к другому участнику?

  • **Google Meet** использует adaptive constraints: при хорошей сети 1080p/30fps, при деградации - applyConstraints() снижает до 360p/15fps без пересоздания соединения. Это позволяет поддерживать 300M+ встреч в день с адаптацией к реальным условиям сети.
  • **Zoom Web Client** (без установки) строится на getUserMedia + getDisplayMedia. Screen sharing поддерживается в Chrome, Firefox, Edge через стандартный API. Safari добавил поддержку getDisplayMedia только в 2022 году - до этого Zoom Web не поддерживал Safari screen share.
  • **Figma** использует getDisplayMedia для функции FigJam screen sharing: участник сессии может показать свой экран прямо в браузере. track.onended отслеживает когда пользователь нажимает 'Stop Sharing' и автоматически убирает превью у всех участников.
  • **Loom** (screen recorder) строится целиком на getDisplayMedia + getUserMedia: захват экрана, веб-камеры и микрофона одновременно, микширование через MediaRecorder API. 14M+ пользователей записывают видео прямо в браузере.

getUserMedia

getUserMedia() - браузерный API для захвата медиа с камеры и микрофона. Возвращает Promise<MediaStream> после того, как пользователь явно разрешил доступ. Разрешение хранится per-origin и запрашивается только один раз; последующие вызовы используют кэшированное разрешение до сброса.

getUserMedia работает только в secure context (HTTPS или localhost). Google Meet запрашивает камеру и микрофон раздельно и gracefully degrading - если камера недоступна, аудио-звонок всё равно работает. Zoom Web требует HTTPS и использует getUserMedia как основу, добавляя собственные noise suppression алгоритмы через WebAudio API.

Пользователь открыл страницу с видеозвонком по HTTP (не HTTPS). Что произойдёт при вызове getUserMedia()?

MediaStreamTrack

MediaStream состоит из MediaStreamTrack объектов - каждый track представляет один поток данных (видео или аудио) от конкретного устройства. Tracks можно включать/выключать, заменять (e.g. смена камеры), клонировать, или применять к ним WebAudio обработку.

track.enabled = false vs track.stop() - принципиальная разница. enabled=false продолжает захват (индикатор камеры горит), но отправляет пустой сигнал. stop() полностью освобождает устройство. Google Meet использует enabled=false для mute - это позволяет быстро unmute без задержки повторного захвата устройства.

Пользователь нажал кнопку 'выключить камеру'. Что правильно: videoTrack.enabled = false или videoTrack.stop()?

MediaTrackConstraints

Constraints позволяют задать требования к захватываемому медиа: разрешение, частоту кадров, выбор конкретного устройства, эхоподавление, noise cancellation. Браузер старается выполнить exact constraints, ideal constraints - по возможности.

Zoom и Google Meet адаптивно меняют constraints в runtime: при деградации сети снижают frameRate через applyConstraints() вместо пересоздания. Agora SDK при инициализации собирает getCapabilities() для определения поддерживаемых разрешений конкретного устройства, и только потом устанавливает constraints.

getUserMedia вызван с video: { width: { exact: 4096 } } (4K), но камера поддерживает максимум 1920px. Что произойдёт?

Screen Sharing

getDisplayMedia() - отдельный API для захвата экрана, окна или вкладки. В отличие от getUserMedia, пользователь сам выбирает источник из системного диалога - приложение не может программно указать конкретное окно. Возвращает MediaStream с video track.

Google Meet переключает camera/screen через replaceTrack() без пересоздания PeerConnection - это zero-downtime смена источника. Zoom Web использует getDisplayMedia с contentHint: 'detail' для экрана (лучше шарпнесс текста) vs contentHint: 'motion' для камеры (лучше плавность видео). Figma позволяет шарить экран через браузер без приложения именно благодаря getDisplayMedia API.

Screen sharing через getDisplayMedia захватывает всё, включая другие вкладки и приложения, без ведома пользователя

Пользователь явно выбирает источник в системном диалоге - конкретную вкладку, окно или экран. Приложение видит только выбранный источник.

getDisplayMedia спроектирован с privacy-first подходом. Диалог выбора управляется браузером/OS, а не страницей. Браузер также показывает постоянный индикатор записи экрана и кнопку 'Stop Sharing', которую пользователь может нажать в любой момент.

Пользователь шарит экран через getDisplayMedia. Приложение хочет автоматически переключиться на конкретное окно по нажатию кнопки, без диалога. Возможно ли это?

Итоги

  • **getUserMedia()** запрашивает доступ к камере/микрофону, работает только на HTTPS. Возвращает MediaStream с независимыми tracks.
  • **track.enabled = false** - mute без остановки устройства (быстрый toggling). **track.stop()** - полное освобождение устройства (нужен новый getUserMedia для включения).
  • **Constraints** позволяют управлять разрешением, fps, AEC/NS/AGC. exact - строгое требование (ошибка если невозможно), ideal - желаемое.
  • **getDisplayMedia()** - отдельный API для screen sharing с обязательным пользовательским диалогом. replaceTrack() меняет источник видео без пересоздания PeerConnection.

Связанные темы

Media Streams - исходный материал для WebRTC пайплайна. Они поступают в PeerConnection и обрабатываются на пути к получателю:

  • STUN и TURN — MediaStream добавляется в RTCPeerConnection через addTrack(); ICE (STUN/TURN) устанавливает канал, по которому эти треки будут переданы
  • SFU архитектура — SFU получает MediaStream от каждого участника и избирательно пересылает нужные треки подписчикам; simulcast использует несколько LayerStream от одного getUserMedia
  • Data Channels — MediaStream tracks (audio/video) и DataChannels мультиплексируются в одном RTCPeerConnection; DataChannel используется для metadata о медиа (mute state, активный спикер)

Вопросы для размышления

  • Почему браузер показывает постоянный индикатор (красная точка) при активном getUserMedia, и как это связано с track.stop()?
  • Zoom позволяет выбирать виртуальный фон. На каком этапе медиапайплайна применяется такой фильтр, и как это сделать через стандартные Web API?
  • В мобильном браузере facingMode: 'environment' даёт заднюю камеру. Как переключить камеру в runtime без пересоздания PeerConnection?

Связанные уроки

  • net-14-udp
Media Streams

0

1

Войти