Video Conferencing System - Tradeoffs and Alternatives
WebRTC Architecture Tradeoffs
SFU vs MCU vs P2P Comparison
Aspect
SFU (Selective Forwarding Unit)
MCU (Multipoint Control Unit)
P2P (Peer-to-Peer)
Server CPU
Low (no transcoding)
High (mixing/transcoding)
None
Client CPU
High (multiple decode)
Low (single decode)
Medium
Bandwidth
High (multiple streams)
Low (single stream)
Highest (mesh)
Scalability
Good (500+ users)
Limited (50-100 users)
Poor (5-10 users)
Quality
Best (original quality)
Degraded (compression)
Variable
Latency
Low (150ms)
Medium (300ms)
Lowest (50ms)
Cost
Medium
High
Low
SFU Architecture (Chosen Approach)
Copy Pros:
✅ Preserves original video quality
✅ Lower server CPU requirements
✅ Scales to large meetings (1000 + participants)
✅ Flexible client-side rendering
✅ Better for screen sharing
Cons:
❌ Higher client CPU usage (multiple decoding)
❌ Higher bandwidth requirements
❌ Complex client implementation
❌ Battery drain on mobile devicesImplementation Details :
Copy
class SFUServer {
forwardStream (fromParticipant, toParticipants, streamType ) {
const stream = fromParticipant.getStream (streamType);
toParticipants.forEach (participant => {
if (participant.wantsStream (streamType, fromParticipant.id )) {
participant.sendStream (stream);
}
});
}
}MCU Alternative
Copy Pros:
✅ Lower client CPU/bandwidth usage
✅ Consistent quality for all participants
✅ Easier client implementation
✅ Better for low-end devices
Cons:
❌ High server CPU requirements
❌ Quality degradation from mixing
❌ Limited scalability
❌ Higher latency
❌ Complex server implementationWhen to Use MCU :
Large webinars (1000+ viewers, few speakers)
Low-bandwidth environments
Legacy device support
Recording optimization
Media Transport Protocols
WebRTC vs Traditional Streaming
Protocol
WebRTC
RTMP
HLS
WebSocket
Latency
150ms
3-5s
10-30s
50ms
Browser Support
Native
Plugin
Native
Native
Firewall Traversal
Excellent
Poor
Good
Good
Scalability
Medium
High
Very High
Low
Quality
Adaptive
Fixed
Adaptive
Variable
Complexity
High
Medium
Low
Low
WebRTC (Chosen for Real-time)
Copy
const peerConnection = new RTCPeerConnection ({
iceServers : [
{ urls : 'stun:stun.l.google.com:19302' },
{
urls : 'turn:turn.example.com:3478' ,
username : 'user' ,
credential : 'pass'
}
]
});
peerConnection.getSenders ().forEach (sender => {
const params = sender.getParameters ();
params.encodings [0 ].maxBitrate = adaptiveBitrate;
sender.setParameters (params);
});HLS Alternative for Recordings
Copy
const hlsConfig = {
segments : 6 ,
targetDuration : 10 ,
adaptiveBitrate : true ,
qualities : [
{ resolution : '1080p' , bitrate : 4000000 },
{ resolution : '720p' , bitrate : 2000000 },
{ resolution : '480p' , bitrate : 1000000 }
]
};Database Architecture Tradeoffs
SQL vs NoSQL for Different Use Cases
Data Type
PostgreSQL (SQL)
Cassandra (NoSQL)
Redis (Cache)
User Profiles
✅ ACID compliance
❌ Eventual consistency
❌ Volatile
Meeting Metadata
✅ Complex queries
❌ Limited queries
❌ Memory limits
Chat Messages
❌ Write scaling
✅ High write throughput
✅ Real-time
Analytics
❌ Time series
✅ Time series optimized
❌ Not persistent
Session State
❌ High latency
❌ Overkill
✅ Fast access
PostgreSQL for Core Data
Copy
BEGIN TRANSACTION;
INSERT INTO meetings (title, host_user_id) VALUES ('Team Call' , 'user123' );
INSERT INTO meeting_participants (meeting_id, user_id, role)
VALUES (currval('meetings_meeting_id_seq' ), 'user123' , 'host' );
COMMIT ;Pros : ACID compliance, complex queries, mature ecosystem
Cons : Write scaling limitations, higher latency
Cassandra Alternative for Analytics
Copy
CREATE TABLE meeting_metrics (
meeting_id UUID ,
timestamp TIMESTAMP ,
metric_name TEXT ,
metric_value DOUBLE ,
PRIMARY KEY (meeting_id, timestamp )
) WITH CLUSTERING ORDER BY (timestamp DESC );Pros : Linear scalability, time-series optimization, high availability
Cons : Limited query flexibility, eventual consistency
Authentication Strategies
JWT vs Session-Based Authentication
Aspect
JWT Tokens
Session Cookies
Scalability
Stateless (better)
Stateful (session store)
Security
Token exposure risk
Server-side validation
Performance
No DB lookup
DB lookup required
Revocation
Complex
Immediate
Mobile Support
Excellent
Limited
Size
Larger payload
Small cookie
JWT Implementation (Chosen)
Copy
const authTokens = {
accessToken : jwt.sign (
{ userId, permissions },
secret,
{ expiresIn : '15m' }
),
refreshToken : jwt.sign (
{ userId, tokenVersion },
refreshSecret,
{ expiresIn : '7d' }
)
};
class AuthManager {
async refreshTokenIfNeeded (token ) {
const decoded = jwt.decode (token);
const timeUntilExpiry = decoded.exp * 1000 - Date .now ();
if (timeUntilExpiry < 5 * 60 * 1000 ) {
return await this .refreshToken ();
}
return token;
}
}OAuth 2.0 Alternative
Copy
const authUrl = `https://auth.example.com/oauth/authorize?
response_type=code&
client_id=${clientId} &
redirect_uri=${redirectUri} &
scope=meetings:read meetings:write&
code_challenge=${codeChallenge} &
code_challenge_method=S256` ;When to Use OAuth : Enterprise SSO, third-party integrations, compliance requirements
Recording Architecture Tradeoffs
Server-Side vs Client-Side Recording
Approach
Server-Side Recording
Client-Side Recording
Quality
Consistent, high
Variable, depends on client
CPU Load
High server load
Distributed to clients
Bandwidth
Efficient (single stream)
Inefficient (multiple uploads)
Reliability
High (controlled environment)
Lower (client failures)
Privacy
Centralized (compliance)
Distributed (privacy)
Cost
High server costs
Lower server costs
Server-Side Recording (Chosen)
Copy class ServerSideRecorder {
async startRecording (meetingId ) {
const compositor = new MediaCompositor ({
width : 1920 ,
height : 1080 ,
framerate : 30 ,
layout : 'grid'
});
const participants = await this .getParticipants (meetingId);
participants.forEach (p => {
compositor.addStream (p.videoStream , p.audioStream );
});
const encoder = new H264Encoder ({ bitrate : 4000000 });
const recordingStream = compositor.pipe (encoder);
return this .saveToStorage (recordingStream, meetingId);
}
}Distributed Recording Alternative
Copy
class DistributedRecorder {
async startClientRecording (participantId ) {
const mediaRecorder = new MediaRecorder (localStream, {
mimeType : 'video/webm;codecs=vp9,opus' ,
videoBitsPerSecond : 2000000
});
mediaRecorder.ondataavailable = (event ) => {
this .uploadChunk (participantId, event.data );
};
}
async stitchRecording (meetingId ) {
const chunks = await this .getRecordingChunks (meetingId);
return this .combineChunks (chunks);
}
}Network Architecture Alternatives
CDN vs Edge Computing vs Regional Deployment
Approach
Global CDN
Edge Computing
Regional Clusters
Latency
Low (cached content)
Lowest (compute at edge)
Medium (regional)
Cost
Medium
High
Low
Complexity
Low
High
Medium
Scalability
Excellent
Good
Limited
Real-time
Poor
Excellent
Good
Edge Computing for WebRTC (Chosen)
Copy
export default {
async fetch (request, env ) {
if (request.url .includes ('/webrtc/' )) {
return handleWebRTCRequest (request, env);
}
return fetch (request);
}
};
async function handleWebRTCRequest (request, env ) {
const country = request.cf .country ;
const colo = request.cf .colo ;
const mediaServer = await findNearestServer (country, colo);
const optimizedRequest = await optimizeForRegion (request, country);
return fetch (mediaServer.url , optimizedRequest);
}Regional Deployment Alternative
Copy
Regions:
us-east-1:
media_servers: 50
signaling_servers: 10
database: primary
eu-west-1:
media_servers: 30
signaling_servers: 6
database: replica
ap-southeast-1:
media_servers: 20
signaling_servers: 4
database: replica Mobile vs Desktop Optimization
Native Apps vs Progressive Web Apps
Platform
Native App
Progressive Web App
Performance
Excellent
Good
Development Cost
High (multiple platforms)
Low (single codebase)
App Store
Required
Optional
Updates
Store approval
Instant
Device Integration
Full access
Limited
Offline Support
Excellent
Good
Battery Usage
Optimized
Higher
Native Mobile Implementation
Copy
class iOSWebRTCManager {
func optimizeForMobile () {
let videoEncoderFactory = RTCDefaultVideoEncoderFactory ()
videoEncoderFactory.preferredCodec = RTCVideoCodecInfo (name: kRTCVideoCodecH264Name)
let constraints = RTCMediaConstraints (
mandatoryConstraints: [
"maxFrameRate" : "15" ,
"maxWidth" : "640" ,
"maxHeight" : "480"
],
optionalConstraints: nil
)
}
}PWA Alternative
Copy
class PWAVideoConf {
async initialize ( ) {
if ('serviceWorker' in navigator) {
await navigator.serviceWorker .register ('/sw.js' );
}
const constraints = {
video : {
width : { ideal : 640 },
height : { ideal : 480 },
frameRate : { ideal : 15 , max : 30 }
},
audio : {
echoCancellation : true ,
noiseSuppression : true ,
autoGainControl : true
}
};
}
}Monitoring and Observability Tradeoffs
Push vs Pull Metrics Collection
Approach
Push (StatsD/DataDog)
Pull (Prometheus)
Network
Higher (constant push)
Lower (on-demand)
Reliability
Fire-and-forget
Guaranteed collection
Scalability
Good
Excellent
Real-time
Excellent
Good
Storage
External service
Self-hosted
Cost
Higher (SaaS)
Lower (self-hosted)
Prometheus Implementation (Chosen)
Copy
const promClient = require ('prom-client' );
const meetingGauge = new promClient.Gauge ({
name : 'active_meetings_total' ,
help : 'Total number of active meetings' ,
labelNames : ['region' , 'server_id' ]
});
const participantHistogram = new promClient.Histogram ({
name : 'meeting_participants_duration' ,
help : 'Duration of participant sessions' ,
buckets : [60 , 300 , 900 , 1800 , 3600 , 7200 ]
});
app.get ('/metrics' , (req, res ) => {
res.set ('Content-Type' , promClient.register .contentType );
res.end (promClient.register .metrics ());
});DataDog Alternative
Copy
const StatsD = require ('node-statsd' );
const client = new StatsD ();
class DataDogMetrics {
recordMeetingJoin (meetingId, participantCount ) {
client.increment ('meeting.join' , 1 , {
meeting_id : meetingId,
region : process.env .AWS_REGION
});
client.gauge ('meeting.participants' , participantCount, {
meeting_id : meetingId
});
}
}Decision Matrix
Architecture Decision Framework
Copy Decision Criteria Weights:
Performance: 30 %
Scalability: 25 %
Cost: 20 %
Complexity: 15 %
Reliability: 10 %
SFU vs MCU Decision:
SFU Score: 8.2 /10
- Performance: 9 /10 (low latency)
- Scalability: 9 /10 (1000+ users)
- Cost: 7 /10 (medium server cost)
- Complexity: 6 /10 (complex client)
- Reliability: 8 /10 (distributed load)
MCU Score: 6.8 /10
- Performance: 7 /10 (higher latency)
- Scalability: 5 /10 (limited users)
- Cost: 5 /10 (high server cost)
- Complexity: 8 /10 (simple client)
- Reliability: 9 /10 (centralized)
Winner: SFU Architecture Technology Selection Summary
Copy Final Architecture Decisions:
Media Architecture: SFU (Selective Forwarding Unit)
Reason: Best balance of quality, scalability, and performance
Transport Protocol: WebRTC
Reason: Native browser support, low latency, adaptive quality
Database Strategy: Polyglot Persistence
- PostgreSQL: Core data (ACID compliance)
- Redis: Sessions and real-time data
- Cassandra: Analytics and time-series
Authentication: JWT with refresh tokens
Reason: Stateless, mobile-friendly, scalable
Recording: Server-side with distributed processing
Reason: Consistent quality, compliance, reliability
Deployment: Edge computing with regional clusters
Reason: Optimal latency with cost efficiency
Monitoring: Prometheus + Grafana
Reason: Open source, scalable, cost-effective