Реалізація WebRTC для відеодзвінків на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація WebRTC для відеодзвінків на сайті
Складна
~1-2 тижні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація WebRTC для відеозвонків на сайті

WebRTC — це не просто «додати відеозвонки». Комплекс протоколів: ICE, STUN, TURN, SDP-negotiation, DTLS-SRTP та медіапайплайн через getUserMedia. Без розуміння кожного компонента отримаєш звонки, що працюють лише у локальній мережі або падають за NAT.

Компоненти WebRTC-стеку

Браузер надає RTCPeerConnection — центральний об'єкт, який керує всім: ICE-кандидатами, медіапотоками, шифруванням. Поверх нього потрібен сигнальний сервер — WebRTC сам не визначає, як два клієнти обмінюються SDP-офферами. Це окрема задача.

Типовий production-стек:

Компонент Варіанти
Сигнальний сервер Socket.IO, WebSocket (Go/Node), Phoenix Channels
ICE/STUN coturn, Twilio STUN, Google STUN
TURN-сервер coturn на VPS, Twilio TURN, Xirsys
Медіасервер (SFU) mediasoup, Janus, LiveKit, Jitsi Videobridge
Клієнтська бібліотека нативний RTCPeerConnection або simple-peer, mediasoup-client

Для P2P-звонків (до 4 учасників) SFU не потрібен. При 5+ учасниках mesh-топологія створює n(n-1)/2 з'єднань на кожного — це неприйнятно. Потрібен SFU.

Архітектура P2P-звонка

Alice                Signal Server              Bob
  |------ offer SDP -------->|                   |
  |                          |------ offer SDP ->|
  |<----- answer SDP --------|                   |
  |                          |<---- answer SDP --|
  |<========= ICE candidates exchange =========>|
  |<============== DTLS handshake ==============>|
  |<======= encrypted RTP/RTCP media stream ====>|

Кожен браузер збирає ICE-кандидатів: host (локальний IP), srflx (публічний IP через STUN), relay (через TURN). TURN-ретрансляція потрібна ~15–20% з'єднань — корпоративні файрволи, симетричний NAT.

ICE та TURN: чому без них нічого не працює

STUN-сервер відповідає на питання «який у мене зовнішній IP?». TURN-сервер проксирує медіатрафік, коли прямое з'єднання неможливе. coturn — стандартний вибір для self-hosted:

# /etc/turnserver.conf
listening-port=3478
tls-listening-port=5349
realm=yourdomain.com
server-name=yourdomain.com
lt-cred-mech
use-auth-secret
static-auth-secret=YOUR_SECRET
total-quota=100
bps-capacity=0
stale-nonce=600
cert=/etc/letsencrypt/live/yourdomain.com/fullchain.pem
pkey=/etc/letsencrypt/live/yourdomain.com/privkey.pem

TURN через TLS на 443 порту обходить більшість корпоративних обмежень.

Сигнальний сервер на Node.js + Socket.IO

io.on('connection', (socket) => {
  socket.on('join-room', (roomId, userId) => {
    socket.join(roomId);
    socket.to(roomId).emit('user-connected', userId);

    socket.on('offer', (offer, targetId) => {
      io.to(targetId).emit('offer', offer, socket.id);
    });

    socket.on('answer', (answer, targetId) => {
      io.to(targetId).emit('answer', answer, socket.id);
    });

    socket.on('ice-candidate', (candidate, targetId) => {
      io.to(targetId).emit('ice-candidate', candidate, socket.id);
    });

    socket.on('disconnect', () => {
      socket.to(roomId).emit('user-disconnected', userId);
    });
  });
});

RTCPeerConnection на клієнті

const pc = new RTCPeerConnection({
  iceServers: [
    { urls: 'stun:stun.yourdomain.com:3478' },
    {
      urls: 'turn:turn.yourdomain.com:3478',
      username: generateTurnUsername(ttl),
      credential: generateTurnCredential(username, secret),
    },
  ],
  iceTransportPolicy: 'all', // 'relay' для примусового TURN
});

// Додаємо медіа
const stream = await navigator.mediaDevices.getUserMedia({
  video: { width: { ideal: 1280 }, height: { ideal: 720 }, frameRate: { ideal: 30 } },
  audio: { echoCancellation: true, noiseSuppression: true, sampleRate: 48000 },
});

stream.getTracks().forEach(track => pc.addTrack(track, stream));

// Negotiation
pc.onicecandidate = ({ candidate }) => {
  if (candidate) socket.emit('ice-candidate', candidate, targetId);
};

pc.onnegotiationneeded = async () => {
  const offer = await pc.createOffer();
  await pc.setLocalDescription(offer);
  socket.emit('offer', offer, targetId);
};

Кодеки та якість

Браузери договариваються про кодеки через SDP. Для відео: VP8, VP9 або H.264; для аудіо: Opus. Примусова встановка переважного кодека через маніпуляцію SDP:

function preferCodec(sdp, codecName) {
  const lines = sdp.split('\n');
  // Знаходимо PT кодека і переставляємо на початок m= секції
  // ...
  return lines.join('\n');
}

const offer = await pc.createOffer();
offer.sdp = preferCodec(offer.sdp, 'VP9'); // VP9 — найкраща якість за тієї ж пропускної здатності
await pc.setLocalDescription(offer);

Адаптивний битрейт через RTCRtpSender.setParameters:

const sender = pc.getSenders().find(s => s.track.kind === 'video');
const params = sender.getParameters();
params.encodings[0].maxBitrate = 800000; // 800 kbps
await sender.setParameters(params);

Simulcast для масштабованих конференцій

При роботі з SFU (mediasoup, LiveKit) використовується Simulcast — клієнт відправляє кілька потоків з різним дозволом:

pc.addTransceiver(videoTrack, {
  direction: 'sendonly',
  sendEncodings: [
    { rid: 'low',  maxBitrate: 150000, scaleResolutionDownBy: 4 },
    { rid: 'mid',  maxBitrate: 500000, scaleResolutionDownBy: 2 },
    { rid: 'high', maxBitrate: 1500000 },
  ],
});

SFU вибирає потрібний слой для кожного отримувача залежно від його пропускної здатності.

Запис звонків

Серверна запис через SFU переважніша за клієнтську. Якщо потрібна клієнтська:

const recorder = new MediaRecorder(stream, {
  mimeType: 'video/webm;codecs=vp9,opus',
  videoBitsPerSecond: 2500000,
});

const chunks = [];
recorder.ondataavailable = e => chunks.push(e.data);
recorder.onstop = () => {
  const blob = new Blob(chunks, { type: 'video/webm' });
  uploadToServer(blob);
};

recorder.start(1000);

Діагностика та моніторинг

getStats() — основний інструмент відладки:

setInterval(async () => {
  const stats = await pc.getStats();
  stats.forEach(report => {
    if (report.type === 'inbound-rtp' && report.kind === 'video') {
      console.log({
        packetsLost: report.packetsLost,
        jitter: report.jitter,
        framesDecoded: report.framesDecoded,
        framesPerSecond: report.framesPerSecond,
      });
    }
  });
}, 2000);

Для production-моніторингу — інтеграція з Datadog WebRTC або відкритий webrtc-internals (chrome://webrtc-internals) під час відладки.

Терміни та трудозатраты

  • P2P відеозвонок з сигнальним сервером — 3–5 днів (два учасники, базові контроли)
  • Групові звонки через SFU (mediasoup/LiveKit) — 2–3 тижні (налаштування сервера, масштабування, кімнати)
  • Запис + постобробка — плюс 1 тиждень
  • Повноцінна конференц-платформа (кімнати, чат, screenshare, запис) — 6–10 тижнів

Сумісність

WebRTC підтримується у всіх сучасних браузерах. Safari підтримує з версії 11, але має обмеження: немає поддержки Insertable Streams у старих версіях, проблеми з renegotiation. iOS вимагає нативного додатку або PWA — браузерний WebRTC на iOS Safari працює з версії 14.5.