Why WebRTC Calls Require a Comprehensive Approach?
WebRTC provides a powerful API for video calls, but a production-ready solution requires more than a single RTCPeerConnection. Direct P2P connection often breaks due to symmetric NAT: up to 15–20% of sessions cannot establish a direct channel. Corporate firewalls blocking UDP add another 10–15% of failures. Without the proper stack (ICE, TURN, signaling server, SFU), calls either fail or deliver unstable quality. We solve these problems: we configure each component for your specific infrastructure so that video calls work stably in any network—from home Wi-Fi to corporate VPNs.
RTCPeerConnection manages ICE candidates, media streams, and DTLS-SRTP encryption. But WebRTC itself does not include SDP offer exchange—a signaling server is required. A typical production stack:
| Component | Options |
|---|---|
| Signaling server | Socket.IO, WebSocket (Go/Node), Phoenix Channels |
| ICE/STUN | coturn, Twilio STUN, Google STUN |
| TURN server | coturn on a dedicated VPS, Twilio TURN, Xirsys |
| Media server (SFU) | mediasoup, Janus, LiveKit, Jitsi Videobridge |
| Client library | native RTCPeerConnection or simple-peer, mediasoup-client |
Without proper configuration of each component, calls will work only locally or fail behind NAT. We integrate WebRTC turnkey, guaranteeing stability.
How a TURN Server Solves the NAT Problem?
A STUN server determines the external IP, but with symmetric NAT it is useless. A TURN server relays media traffic when P2P is impossible. We deploy coturn with TLS on port 443. This increases traversal through corporate proxies by 3 times compared to UDP TURN. Configuration:
coturn deployment with TLS
# /etc/turnserver.conf
listening-port=3478
tls-listening-port=5349
realm=your-domain.com
server-name=your-domain.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/your-domain.com/fullchain.pem
pkey=/etc/letsencrypt/live/your-domain.com/privkey.pem
For high loads (100+ concurrent calls), we use coturn clustering with DNS round-robin. Each instance holds up to 100 sessions.
What is an SFU and When is It Needed?
SFU (Selective Forwarding Unit) is a media server that forwards streams without mixing. Unlike mesh topology (P2P), where 5 participants create 10 connections per client, an SFU receives one stream and relays it to recipients. Client load is reduced by 9 times with 10 participants. We use mediasoup or LiveKit depending on performance and flexibility requirements.
| Parameter | P2P (mesh) | SFU |
|---|---|---|
| Max participants | 4-6 | 50+ |
| Client requirements | High (n-1 connections) | Low (1 connection) |
| Server load | Zero | Medium |
| Complexity | Low | Medium |
| Call recording | Client-only | Server-side |
SFU allows serving 10 times more participants with the same bandwidth.
Signaling Server: Implementation on Node.js + Socket.IO
The signaling server relays SDP offers, answers, and ICE candidates between participants. Example server-side code:
// server.js
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);
});
});
});
This code is the basis for P2P and SFU calls. A TURN server costs about $10–20 per month for a small project, and traffic savings from Simulcast reach 50%.
Client Side: RTCPeerConnection and Media Management
On the client, we create an RTCPeerConnection with ICE/TURN servers and obtain a media stream:
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.your-domain.com:3478' },
{
urls: 'turn:turn.your-domain.com:3478',
username: generateTurnUsername(ttl),
credential: generateTurnCredential(username, secret),
},
],
iceTransportPolicy: 'all',
});
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));
To improve quality, we use Simulcast with SFU—the client sends three streams of different resolutions:
pc.addTransceiver(videoTrack, {
direction: 'sendonly',
sendEncodings: [
{ rid: 'low', maxBitrate: 150000, scaleResolutionDownBy: 4 },
{ rid: 'mid', maxBitrate: 500000, scaleResolutionDownBy: 2 },
{ rid: 'high', maxBitrate: 1500000 },
],
});
The SFU selects the appropriate layer for each receiver, delivering the best quality under limited bandwidth. Additionally, we monitor connection state through oniceconnectionstatechange and onconnectionstatechange handlers, implementing automatic reconnection during temporary failures.
Monitoring and Optimization
After launch, it is crucial to track real call metrics. We use getStats() to collect key indicators:
- packetsLost – packet loss percentage; if exceeds 5%, we initiate adaptive bitrate reduction.
- jitter – jitter; if >30ms, we switch audio codec to Opus with lower latency.
- roundTripTime – RTT; high RTT signals a need to change TURN server or route.
Note: as stated in the WebRTC API documentation, this data helps quickly identify problematic sessions and adjust configuration.
What's Included: Deliverables
After implementation, you receive:
- architectural documentation with topology, servers, and flows;
- source code of the signaling server and client integration (Git repository);
- access to the TURN server (time-limited credentials);
- team training (2-hour session on operation);
- one month of post-release support (monitoring via
getStats(), incident resolution).
Implementation Process and Timelines
WebRTC implementation proceeds in five stages:
- Analysis – audit of current infrastructure, use cases, expected load.
- Design – selection of topology (P2P or SFU), servers, codecs.
- Implementation – configuration of TURN/STUN, signaling server, client integration.
- Testing – load testing, different networks, mobile devices.
- Deployment – go-live, monitoring via
getStats().
Timelines:
- P2P video call with signaling server – 3 to 5 days.
- Group calls via SFU – 2 to 3 weeks.
- Recording and post-processing – plus 1 week.
- Full conferencing platform – 6 to 10 weeks.
Pricing is determined after the audit. Contact us for a free consultation—we will evaluate your project and propose the optimal architecture. Order WebRTC implementation and get stable video calls in any network.







