A real-time video communication app with two modes:
- Live Mode: real-time two-way audio/video call using mediasoup SFU.
- Recording Mode: short video message exchange (record -> chunk stream -> playback).
No login is required. Users join by sharing a room ID.
- Room-based joining with shared room ID.
- No authentication.
- Live Mode supports group calling (multiple participants in the same room).
- Recording Mode is also 1-to-1 session based.
- Frontend: React, Vite, Tailwind CSS, socket.io-client, mediasoup-client.
- Backend: Node.js, Express, socket.io, mediasoup.
- Transport: WebRTC media + Socket.IO signaling.
Video_Record_Call/
Client/ # React + Vite frontend
Server/ # Express + Socket.IO + mediasoup backend
constants.js # Shared client/server addresses
ssl/ # Optional SSL cert/key (currently commented)
README.md
- A user enters a room ID in Live or Record lobby.
- Client emits join events to server.
- Server keeps in-memory room/peer state:
rooms: room -> router + peer socket IDspeers: socket -> transports/producers/consumerstransports,producers,consumers: flat arrays for lookup/cleanup
On backend startup:
- Creates one mediasoup worker with RTP port range
2000-2120. - Creates routers per room (lazily, on first join).
Live call sequence:
- Client joins room (
joinRoom) and receives router RTP capabilities. - Client creates mediasoup
Devicefrom those capabilities. - Client asks server for send transport (
createWebRTCTransport,consumer: false). - Client connects send transport (
producerTransport-connect). - Client produces local audio and video (
producerTransport-produce) as separate producers. - Server informs other peers with
new-producer. - Other peers create recv transport (
consumer: true), connect, and consume (consumerTransport-consume). - Client resumes consumer (
consumer-resume) and renders remote stream.
Cleanup:
- On
disconnect, server removes all transports/producers/consumers for that socket. - If room becomes empty, room is deleted.
- Sender records local stream via
MediaRecorder. - Recorded chunks are emitted to server (
streamData1-record:server). - Server forwards chunks to target peer (
streamData1-record:client). - Receiver accumulates chunks and plays a generated Blob URL when final chunk arrives.
- No file persistence/database storage is used.
GET /-> serves SPA index fromServer/dist.GET /live-> serves SPA index.GET /record-> serves SPA index.GET /states-> debug JSON with current mediasoup object counts and IDs.
join-user-recorduser-joined-recorduser-joined-confirm-record:serveruser-joined-confirm-record:clientstreamData1-record:serverstreamData1-record:clientend-call-recorduser-left-record
join-useruser-joineduser-joined-confirm:serveruser-joined-confirm:clientjoinRoomcreateWebRTCTransportproducerTransport-connectproducerTransport-producenew-producergetProducersconsumerTransport-connectconsumerTransport-consumeconsumer-resumeproducer-closed
- Node.js 18+ (recommended 20+)
- npm
- Camera/microphone permissions
cd Client && npm install
cd ../Server && npm installUpdate constants.js:
SERVER_ADDRESS: backend URL used by socket.io client.CLIENT_ADDRESS: allowed Socket.IO CORS origin.
For local testing example:
export const SERVER_ADDRESS = "http://localhost:3000"
export const CLIENT_ADDRESS = "http://localhost:5173"In Server/.env set at least:
PORT=3000MEDIASOUP_LISTEN_IP=0.0.0.0MEDIASOUP_ANNOUNCED_IP=<your-public-ip-or-host-ip>
Important:
- If clients are on different networks/machines,
MEDIASOUP_ANNOUNCED_IPmust be reachable by them. - If this is wrong, signaling can succeed but media may fail.
Terminal 1:
cd Server
npm run devTerminal 2:
cd Client
npm run devOpen http://localhost:5173.
Backend serves static files from Server/dist.
Build frontend and copy artifacts to backend dist:
cd Client
npm run build
cd ..
rm -rf Server/dist
cp -r Client/dist Server/distThen run backend:
cd Server
node index.jsApp will be served from backend root routes (/, /live, /record).
- Signaling port:
3000(or configured server port). - RTP/RTCP port range:
2000-2120(UDP/TCP as configured). - Ensure firewall/security group allows the mediasoup RTP port range.
- No auth/authorization layer exists currently.
- Room IDs are guessable by design in current implementation.
- Do not commit real secrets/tokens to source control.
- Live mode can run group calls, but room quality depends on client/network capacity.
- In-memory room state only (no persistence/recovery after server restart).
constants.jsuses hard-coded URLs (manual env-based config not yet wired).- HTTPS server setup is present but commented out.
- Check
GET /statesto inspect active transports/producers/consumers. - If video does not flow but socket events do, verify:
MEDIASOUP_ANNOUNCED_IP- Open RTP port range
2000-2120 - Browser camera/mic permissions
- If client cannot connect to socket, verify
SERVER_ADDRESSandCLIENT_ADDRESSmatch running origins.
Client (Client/package.json):
npm run devnpm run buildnpm run previewnpm run lint
Server (Server/package.json):
npm run dev(Node watch mode)
- Better UX and controls for large group calls (participant list, active speaker, moderation).
- Authentication and room access controls.
- Persisted recording storage (S3/local/object storage).
- Env-based runtime config instead of hard-coded
constants.jsURLs. - TURN/STUN hardening for stricter NAT environments.