WebRTC-Demo is a comprehensive, cross-platform WebRTC demonstration project that showcases real-time peer-to-peer communication capabilities across Web, Android, and iOS platforms. The project implements modern WebRTC APIs (currently using M125) to enable video calls, audio communication, screen sharing, and data channel messaging between multiple clients.
The project follows a client-server architecture with the following components:
┌─────────────────────────────────────────────────────────────┐
│ Clients │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Web │ │ Android │ │ iOS │ │
│ │ (Vue.js) │ │ (Native)│ │ (Native) │ │
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
│ │ │ │ │
│ └───────────────┴───────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Signaling │ │
│ │ Server │ │
│ │ (Node.js + │ │
│ │ Socket.io) │ │
│ └─────────────────┘ │
│ │
│ P2P Connection (WebRTC - STUN/TURN) │
│ ════════════════════════════════════ │
│ Client A ◄──────────────────► Client B │
└─────────────────────────────────────────────────────────────┘
Technology Stack:
- Node.js
- Express.js
- Socket.io
Purpose: The signaling server facilitates the initial peer discovery and exchange of connection information (SDP offers/answers and ICE candidates) between WebRTC clients. It does not handle the actual media streaming.
Key Features:
- Room-based architecture (max 2 participants per room)
- WebSocket-based real-time communication
- SDP offer/answer exchange
- ICE candidate relay
- Room management and participant tracking
Protocol Flow:
- Join Room: Client connects and joins a room by ID
- Offer Exchange: First client creates offer when second client joins
- Answer Exchange: Second client responds with answer
- ICE Candidate Exchange: Both peers exchange ICE candidates
- P2P Connection: Direct peer-to-peer connection established
Server Events:
join room- Client joins a specific roomoffer- Send WebRTC offer to peeranswer- Send WebRTC answer to peernew ice candidate- Exchange ICE candidatesreceive encryption key- Exchange E2EE keys (web only)send data channel message- Relay data channel messagesleave room- Leave the current room
Technology Stack:
- Vue.js 3 (Composition API)
- TypeScript
- Vite (build tool)
- Socket.io-client
- Native WebRTC API
Architecture:
App.vue
├── WebRTC PeerConnection Management
├── Socket.io Connection
├── Media Stream Management
│ ├── Local Camera/Microphone
│ ├── Screen Sharing
│ └── Remote Stream
├── Data Channel
└── E2EE (End-to-End Encryption)
├── Main Thread Implementation
└── Web Worker Implementation
Key Features:
-
Audio/Video Control
- Toggle microphone on/off
- Toggle camera on/off
- Switch between front/back camera (mobile)
- Switch between microphone and speaker output
-
Screen Sharing
- Desktop/tab/window sharing via
getDisplayMedia() - Automatic track replacement in peer connection
- Switch back to camera functionality
- Desktop/tab/window sharing via
-
Data Channel Messaging
- Real-time text messaging
- Low-latency data transfer
- Chat interface for peer-to-peer communication
-
End-to-End Encryption (E2EE)
- Current Status: Only supported for Web-Web communication
- Algorithm: AES-GCM with 256-bit keys
- Implementation Options:
- Main thread encryption/decryption
- Web Worker-based encryption (offload processing)
E2EE Architecture:
Sender Receiver ────── ──────── Media Frame │ ▼ Generate IV (12 bytes) │ ▼ AES-GCM Encrypt │ ▼ Append IV to encrypted data │ ▼ Send via WebRTC ──────────► Extract IV │ ▼ AES-GCM Decrypt │ ▼ Render FrameE2EE Process:
- Initiator generates AES-GCM encryption key
- Key is exported and sent to remote peer via signaling server
- Both peers use Insertable Streams API to:
- Encrypt outgoing media frames
- Decrypt incoming media frames
- Each frame gets unique IV (Initialization Vector)
- Debug Mode:
shouldSendEncryptionKey=falseshows raw encrypted video
Files:
e2ee.ts- Encryption/decryption stream transformersencryptionWorker.ts- Web Worker for offloading crypto operations
-
Media Streams API Integration
getUserMedia()for camera/microphone accessgetDisplayMedia()for screen sharing- Track replacement for switching sources
Technology Stack:
- Swift
- WebRTC framework (native)
- UIKit
- ReplayKit (for screen broadcasting)
Project Structure:
WebRTCDemo (Main App)
├── AppDelegate.swift
├── SceneDelegate.swift
├── ViewController.swift (Main UI)
├── CallViewController.swift (Call management)
├── PeerConnectionClient.swift (WebRTC logic)
├── CameraSession.swift (Camera management)
└── RTCCustomFrameCapturer.swift (Custom video capture)
WebRTCDemoScreenBroadcast (Broadcast Extension)
└── SampleHandler.swift (Screen capture handler)
WebRTCDemoScreenBroadcastSetupUI (Broadcast Setup)
└── BroadcastSetupViewController.swift
Key Components:
-
PeerConnectionClient.swift
- Manages RTCPeerConnection lifecycle
- Handles ICE candidate generation and exchange
- Video/audio track management
- Data channel creation and messaging
- Camera switching (front/back)
- Local and remote video rendering
-
CallViewController.swift
- Main call UI controller
- WebSocket connection management
- Room joining/leaving logic
- UI controls for audio/video/data channel
-
Screen Broadcasting
- Uses ReplayKit framework
- Broadcast Extension for system-level screen capture
- Separate process for privacy and security
Key Features:
- Native WebRTC SDK integration
- Camera switching
- Audio routing (speaker/earpiece)
- Data channel messaging
- Full-screen broadcasting (via Broadcast Extension)
- Unix domain socket communication between extension and main app
- Automatic track switching for screen sharing
Screen Broadcasting Architecture:
- Uses
RPBroadcastSampleHandlerfor system-wide screen capture - Broadcast Extension captures and encodes frames to JPEG
- Unix domain socket (via App Group) transfers frames to main app
FlutterBroadcastScreenCapturerfeeds frames into WebRTC- Darwin notifications coordinate lifecycle between processes
- Automatic camera ↔ screen track switching
Configuration:
- Server URL configured in
CallViewController.swift - Minimum deployment target considerations for dependencies
Technology Stack:
- Kotlin/Java (native Android)
- WebRTC framework for Android
- Gradle build system
Project Structure:
android/
├── app/
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ └── res/
├── build.gradle.kts
├── settings.gradle.kts
└── gradle/
Key Features:
- Native WebRTC integration for Android
- Camera and microphone access
- Screen sharing (device screen capture)
- Data channel messaging
- Audio/video controls
Configuration:
- Server address configured in
res/values/strings.xml - Permissions for camera, microphone, and internet in AndroidManifest.xml
1. Initialize RTCPeerConnection
│
▼
2. Add Local Media Tracks (getUserMedia)
│
▼
3. Create Offer (Caller) / Wait for Offer (Callee)
│
▼
4. Set Local Description (SDP)
│
▼
5. Send SDP to Remote Peer (via signaling)
│
▼
6. Receive Remote SDP & Set Remote Description
│
▼
7. Exchange ICE Candidates
│
▼
8. ICE Connection Established
│
▼
9. Media Flows Directly Between Peers
The project uses Google's public STUN server:
- STUN Server:
stun:stun.l.google.com:19302 - Purpose: NAT traversal, discover public IP addresses
- No TURN server configured (may fail behind restrictive NATs)
- Use Case: Text messaging between peers
- Reliability: Configurable (reliable/unreliable)
- Ordering: Can be ordered or unordered
- Low Latency: Direct P2P, bypasses signaling server
- Node.js and npm/yarn
- iOS: Xcode, CocoaPods
- Android: Android Studio, Android SDK
- Modern web browser with WebRTC support
-
Start Signaling Server
cd signaling-server npm install npm run devServer runs on port 4000 and displays local IP address
-
Start Web Client
cd web npm install npm run devAvailable at
http://localhost:5173 -
iOS Setup
cd ios pod install- Open
WebRTCDemo.xcworkspacein Xcode - Update
SERVER_URLinCallViewController.swift - Build and run
- Open
-
Android Setup
- Update
serverAddressinandroid/app/src/main/res/values/strings.xml - Open project in Android Studio
- Build and run
- Update
- Start signaling server and note the IP address
- Launch any two clients (Web, iOS, or Android)
- Enter the same Room ID on both clients
- First client waits in the room
- Second client joins and connection is established
- Start communicating with audio, video, and messages
The E2EE implementation uses the Insertable Streams API (also known as WebRTC Encoded Transform):
// For sender (encoding)
const senderStreams = sender.createEncodedStreams();
encryptStream(encryptionKey,
senderStreams.readable,
senderStreams.writable);
// For receiver (decoding)
const receiverStreams = receiver.createEncodedStreams();
decryptStream(encryptionKey,
receiverStreams.readable,
receiverStreams.writable);Transform Pipeline:
- Intercept encoded video/audio frames
- Apply AES-GCM encryption/decryption
- IV (Initialization Vector) is prepended to each frame
- Frames remain opaque to intermediaries
Worker Architecture (optional):
Main Thread Encryption Worker
─────────── ─────────────────
Generate Key ──────────────► Store Key
Encoded Frame ────────────► Encrypt Frame
│
▼
Encrypted Frame ◄───────── Return Encrypted
Rooms are temporary and in-memory:
- Maximum Capacity: 2 participants
- Lifecycle: Created when first user joins, implicitly destroyed when empty
- Identification: String-based room IDs
- Validation: Prevents duplicate joins, full room notifications
Web Client Flow:
- Request permissions:
navigator.mediaDevices.getUserMedia() - Create local stream with constraints (resolution, frame rate)
- Add tracks to peer connection:
peerConnection.addTrack() - Display local preview:
videoElement.srcObject = stream - Receive remote stream via
trackevent - Render remote video
Native Clients:
- Use platform-specific WebRTC SDK APIs
- Similar flow with native API equivalents
- Hardware acceleration for encoding/decoding
| Feature | Web | Android | iOS |
|---|---|---|---|
| Audio Control | ✅ | ✅ | ✅ |
| Video Control | ✅ | ✅ | ✅ |
| Camera Switching | ✅ | ✅ | ✅ |
| Speaker/Microphone Toggle | ✅ | ✅ | ✅ |
| Screen Sharing | ✅ | ✅ | ✅ |
| Data Channel Messaging | ✅ | ✅ | ✅ |
| End-to-End Encryption | ✅ | 🚧 | 🚧 |
| Stream Video File | 🚧 | 🚧 | 🚧 |
✅ = Supported | ❌ = Not Supported | 🚧 = In Progress
Issue 1: Minimum Deployment Target
- Error: "Compiling for iOS 11.0, but module..."
- Solution: Update pod's Minimum Deployment to latest iOS version in Xcode project settings
Issue 2: Sandbox rsync Error
- Error:
Sandbox: rsync.samba(13105)... - Solution: Set
ENABLE_USER_SCRIPT_SANDBOXINGtoNoin Xcode build options
- Connection Fails: Check firewall settings, ensure signaling server is accessible
- No Video/Audio: Verify permissions granted for camera/microphone
- NAT Traversal: May need TURN server for restrictive networks
- E2EE Performance: Use Web Worker mode to prevent UI freezing
-
E2EE for Native Platforms
- Android: Implement frame encryption using native WebRTC frame cryptor
- iOS: Similar native implementation
-
Stream Video Files
- Play video files to remote peer
- Use
RTCVideoSourcewith custom capturer
-
Multi-Party Conferencing
- Mesh, SFU, or MCU architecture
- More than 2 participants per room
-
Recording
- Record calls locally
- Server-side recording option
-
TURN Server Integration
- Better connectivity in restrictive networks
- Fallback for failed STUN connections
-
Advanced Features
- Simulcast for adaptive bitrate
- SVC (Scalable Video Coding)
- Background blur/virtual backgrounds
- Noise suppression
- E2EE: Only web-to-web, prevents MITM attacks on media
- Signaling: Unencrypted WebSocket (not production-ready)
- Authentication: No user authentication implemented
- Room Access: Anyone with room ID can join
- Use WSS (WebSocket Secure) for signaling
- Implement authentication (JWT, OAuth)
- Room access control with passwords or invitations
- HTTPS for web client
- Complete E2EE across all platforms
- Validate and sanitize all inputs
- Rate limiting on signaling server
- Separation of Concerns: WebRTC logic separated from UI
- Reusability: Platform-specific wrappers around WebRTC
- Error Handling: Comprehensive error handling for network issues
- Logging: Debug logs for troubleshooting
- Local Testing: Multiple browser tabs/windows
- Network Testing: Different networks (WiFi, cellular)
- Cross-Platform: Test web-to-native, native-to-native
- Load Testing: Signaling server under multiple rooms
- Edge Cases: Disconnections, reconnections, poor network
When contributing to this project:
- Follow the existing code style
- Test across all platforms when possible
- Update this documentation for significant changes
- Handle errors gracefully
- Add logging for debugging purposes
Refer to the LICENSE file in the repository root.
This project is intended for educational and demonstration purposes to showcase WebRTC capabilities across multiple platforms. It may contain bugs and is not production-ready. Use with caution in production environments.
Last Updated: January 2026
WebRTC Version: M125
Maintainer: Project repository maintainers