W3docs

WebRTC

WebRTC (Web Real-Time Communication) — открытый стандарт и набор браузерных API для передачи аудио, видео и данных напрямую между браузерами в реальном времени.

WebRTC: коммуникация в реальном времени в браузере

WebRTC (Web Real-Time Communication) — открытый стандарт и набор браузерных API, позволяющих двум браузерам обмениваться аудио, видео и произвольными данными напрямую друг с другом — по принципу peer-to-peer — без маршрутизации медиапотока через сервер и без каких-либо плагинов. На его основе работают видеоконференции, голосовые звонки, демонстрация экрана, синхронизация игрового состояния и передача файлов с низкой задержкой.

В этой главе рассматривается, что даёт WebRTC, три API, из которых он состоит, концепции сигнализации и обхода NAT, вызывающие замешательство у большинства новичков, а также полный рабочий пример с каналом данных, который можно запустить прямо на этой странице.

Что WebRTC делает на самом деле

Браузер уже умеет общаться с сервером (см. Fetch API и WebSockets). WebRTC добавляет недостающее звено: способ для двух клиентов общаться друг с другом. После установки peer-соединения медиа и данные передаются по кратчайшему возможному пути — зачастую напрямую между двумя машинами, — именно поэтому задержка WebRTC значительно ниже, чем при ретрансляции всего через бэкенд.

Типичные случаи использования:

  • Аудио- и видеозвонки — голос/видео в реальном времени, как в Zoom или Google Meet.
  • Демонстрация экрана — захват экрана или окна с помощью getDisplayMedia() и стриминг партнёру.
  • Данные в реальном времени — игровое состояние, чат, позиции курсора или фрагменты файлов через RTCDataChannel.

Зачем использовать WebRTC

  • Peer-to-peer, низкая задержка. Медиапоток идёт напрямую между участниками, минуя ваш сервер — время прохождения сигнала невелико, а расходы на трафик остаются небольшими.
  • Без плагинов. Встроен в каждый современный браузер — никакого Flash, нативных приложений или загрузок.
  • Шифрование по умолчанию. Медиа использует SRTP, каналы данных — DTLS; шифрование обязательно и не может быть отключено.
  • Кроссплатформенность. Работает в настольных и мобильных браузерах, а также в нативных приложениях по тому же стандарту.
  • Открытый стандарт. Поддерживается W3C и IETF, что обеспечивает постоянное развитие без привязки к конкретному вендору.

Три основных API

WebRTC — это три API, работающих вместе:

  1. MediaStream API (getUserMedia) — получает аудио/видео с микрофона, камеры или экрана в виде MediaStream.
  2. RTCPeerConnection — сердце WebRTC. Выполняет согласование, шифрование и поддержку соединения между двумя участниками, передавая медиадорожки.
  3. RTCDataChannel — двунаправленный канал для отправки произвольных данных (строк или бинарных) по принципу peer-to-peer, аналогично WebSockets, но без сервера посередине.

Сигнализация: то, чего WebRTC не делает

Два браузера не могут найти друг друга из ниоткуда. Прежде чем peer-соединение откроется, участники должны обменяться двумя видами информации:

  • SDP (Session Description Protocol) — «предложение» и «ответ», описывающие кодеки, форматы медиа и параметры соединения.
  • ICE-кандидаты — возможные сетевые адреса (пары IP/порт), по которым каждый из участников доступен.

WebRTC не определяет, как происходит этот обмен — это ваша задача, и называется она сигнализацией. Вы создаёте канал сигнализации с помощью любого серверного транспорта; WebSockets — наиболее распространённый выбор. Процесс всегда выглядит так:

  1. Инициатор создаёт предложение (offer) и отправляет его через сервер сигнализации.
  2. Получатель принимает предложение, создаёт ответ (answer) и отправляет его обратно.
  3. Оба участника обмениваются ICE-кандидатами по мере их обнаружения.
  4. Прямое peer-to-peer соединение установлено; сервер сигнализации больше не нужен.

STUN и TURN: преодоление брандмауэров

Большинство устройств находятся за NAT (Network Address Translation) и брандмауэрами, поэтому участник редко знает свой публичный адрес. Эту проблему решают два типа серверов:

  • STUN-сервер — сообщает участнику его публичный IP/порт, чтобы пиры могли попытаться установить прямое соединение. Дёшев и не имеет состояния; Google предоставляет бесплатные публичные серверы.
  • TURN-сервер — ретранслирует медиапоток, когда прямое соединение невозможно (строгие корпоративные брандмауэры). Потребляет трафик, поэтому используется как резервный вариант, а не по умолчанию.

Эти серверы указываются в конфиге iceServers, передаваемом в RTCPeerConnection:

const configuration = {
  iceServers: [
    { urls: 'stun:stun.l.google.com:19302' },
    // A TURN server is added for hard-to-reach networks:
    // { urls: 'turn:turn.example.com', username: 'user', credential: 'pass' }
  ],
};

const peerConnection = new RTCPeerConnection(configuration);

Пример: захват камеры с getUserMedia

Первый шаг любого видеозвонка — получение локального потока. getUserMedia возвращает Promise, поэтому можно использовать async/await:

<video id="localVideo" autoplay playsinline muted></video>
const localVideo = document.getElementById('localVideo');

async function startCamera() {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true,
    });
    localVideo.srcObject = stream; // show the live camera feed
    return stream;
  } catch (error) {
    console.error('Could not access camera/microphone:', error.message);
  }
}

startCamera();

getUserMedia работает только в безопасном контексте (HTTPS или localhost) и запрашивает у пользователя разрешение — незаметно записать кого-либо невозможно.

Пример: полный канал данных между двумя участниками

Нагляднее всего увидеть работу WebRTC можно, соединив два объекта RTCPeerConnection в одном скрипте. Здесь оба участника находятся на одной странице, поэтому сигнализацию можно выполнить напрямую, без сервера — обмен SDP/ICE в точности соответствует тому, что передавал бы реальный сервер сигнализации. Вставьте этот код в консоль браузера на любой HTTPS-странице и наблюдайте за его работой:

// Two peers, normally on different machines.
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();

// --- Signaling: hand each peer's ICE candidates to the other.
// In production this travels over WebSockets; here we call directly.
caller.onicecandidate = (e) => e.candidate && callee.addIceCandidate(e.candidate);
callee.onicecandidate = (e) => e.candidate && caller.addIceCandidate(e.candidate);

// The callee listens for the channel the caller opens.
callee.ondatachannel = (event) => {
  const channel = event.channel;
  channel.onmessage = (e) => {
    console.log('Callee received:', e.data);
    channel.send('pong'); // reply over the same channel
  };
};

// The caller creates the data channel.
const channel = caller.createDataChannel('chat');
channel.onopen = () => channel.send('ping');
channel.onmessage = (e) => console.log('Caller received:', e.data);

// --- Offer / answer negotiation.
async function connect() {
  const offer = await caller.createOffer();
  await caller.setLocalDescription(offer);
  await callee.setRemoteDescription(offer);

  const answer = await callee.createAnswer();
  await callee.setLocalDescription(answer);
  await caller.setRemoteDescription(answer);
}

connect();
// Logs:
// Callee received: ping
// Caller received: pong

Для видео паттерн аналогичный: вместо createDataChannel вызывается peerConnection.addTrack(track, stream) для каждой дорожки из getUserMedia, а входящий медиапоток обрабатывается в обработчике ontrack:

const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
stream.getTracks().forEach((track) => peerConnection.addTrack(track, stream));

// The remote peer's media arrives here:
peerConnection.ontrack = (event) => {
  document.getElementById('remoteVideo').srcObject = event.streams[0];
};

Распространённые подводные камни

  • Требуется HTTPS. getUserMedia и getDisplayMedia не работают вне безопасного контекста (HTTPS или localhost).
  • Сервер сигнализации всё равно нужен. WebRTC берёт на себя передачу медиа, но обмен offer/answer/ICE вы должны реализовать самостоятельно — как правило, через WebSockets.
  • Всегда создавайте offer/answer в правильном порядке. setLocalDescription должен выполниться раньше setRemoteDescription для соответствующей стороны, иначе согласование завершится ошибкой.
  • Не забудьте TURN-сервер в продакшене. Одного STUN недостаточно в сетях с ограничениями; без TURN около 10–20% реальных соединений не будут установлены.
  • Разрешения могут быть отклонены. Оборачивайте getUserMedia в try/catch и корректно обрабатывайте отказ.

Итоги

WebRTC даёт браузеру настоящую peer-to-peer передачу аудио, видео и данных. Используйте getUserMedia для захвата медиа, RTCPeerConnection для согласования и поддержки соединения и RTCDataChannel для произвольных данных. WebRTC не обеспечивает сигнализацию — обмен SDP offer/answer и ICE-кандидатами выполняется вами самостоятельно, как правило через WebSockets, а STUN/TURN-серверы настраиваются для обхода NAT. Имея эти компоненты на месте, вы можете создавать видеозвонки, демонстрацию экрана и совместную работу в реальном времени полностью в браузере.

Практика

Практика
Какие из следующих утверждений о возможностях WebRTC верны?
Какие из следующих утверждений о возможностях WebRTC верны?
Was this page helpful?