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, работающих вместе:
- MediaStream API (
getUserMedia) — получает аудио/видео с микрофона, камеры или экрана в видеMediaStream. - RTCPeerConnection — сердце WebRTC. Выполняет согласование, шифрование и поддержку соединения между двумя участниками, передавая медиадорожки.
- RTCDataChannel — двунаправленный канал для отправки произвольных данных (строк или бинарных) по принципу peer-to-peer, аналогично WebSockets, но без сервера посередине.
Сигнализация: то, чего WebRTC не делает
Два браузера не могут найти друг друга из ниоткуда. Прежде чем peer-соединение откроется, участники должны обменяться двумя видами информации:
- SDP (Session Description Protocol) — «предложение» и «ответ», описывающие кодеки, форматы медиа и параметры соединения.
- ICE-кандидаты — возможные сетевые адреса (пары IP/порт), по которым каждый из участников доступен.
WebRTC не определяет, как происходит этот обмен — это ваша задача, и называется она сигнализацией. Вы создаёте канал сигнализации с помощью любого серверного транспорта; WebSockets — наиболее распространённый выбор. Процесс всегда выглядит так:
- Инициатор создаёт предложение (offer) и отправляет его через сервер сигнализации.
- Получатель принимает предложение, создаёт ответ (answer) и отправляет его обратно.
- Оба участника обмениваются ICE-кандидатами по мере их обнаружения.
- Прямое 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. Имея эти компоненты на месте, вы можете создавать видеозвонки, демонстрацию экрана и совместную работу в реальном времени полностью в браузере.