POST /janus { "janus": "create", "transaction": "tx-001" } // Response: {"janus": "success", "data": {"id": 3718046820721403}}
POST /janus { "janus": "create", "transaction": "tx-001" } // Response: {"janus": "success", "data": {"id": 3718046820721403}}
POST /janus { "janus": "create", "transaction": "tx-001" } // Response: {"janus": "success", "data": {"id": 3718046820721403}}
POST /janus/{session_id} { "janus": "attach", "plugin": "janus.plugin.videoroom", "transaction": "tx-002"
}
POST /janus/{session_id} { "janus": "attach", "plugin": "janus.plugin.videoroom", "transaction": "tx-002"
}
POST /janus/{session_id} { "janus": "attach", "plugin": "janus.plugin.videoroom", "transaction": "tx-002"
}
{ "janus": "message", "transaction": "tx-010", "body": { "request": "create", "room": 1234, "publishers": 12, "bitrate": 1500000, "notify_joining": false, "record": true, "lock_record": true }
}
{ "janus": "message", "transaction": "tx-010", "body": { "request": "create", "room": 1234, "publishers": 12, "bitrate": 1500000, "notify_joining": false, "record": true, "lock_record": true }
}
{ "janus": "message", "transaction": "tx-010", "body": { "request": "create", "room": 1234, "publishers": 12, "bitrate": 1500000, "notify_joining": false, "record": true, "lock_record": true }
}
GET /janus/{session_id}?maxev=5
GET /janus/{session_id}?maxev=5
GET /janus/{session_id}?maxev=5
{ "janus": "keepalive", "session_id": 3718046820721403, "transaction": "tx-ka-001"
}
{ "janus": "keepalive", "session_id": 3718046820721403, "transaction": "tx-ka-001"
}
{ "janus": "keepalive", "session_id": 3718046820721403, "transaction": "tx-ka-001"
}
{ "request": "enable_recording", "room": 1234, "secret": "room-secret-XYZ", "record": true
}
{ "request": "enable_recording", "room": 1234, "secret": "room-secret-XYZ", "record": true
}
{ "request": "enable_recording", "room": 1234, "secret": "room-secret-XYZ", "record": true
}
# Audio: MJR to Opus
janus-pp-rec /recordings/room1234-user42-audio.mjr /output/user42-audio.opus # Video: MJR to WebM
janus-pp-rec /recordings/room1234-user42-video.mjr /output/user42-video.webm
# Audio: MJR to Opus
janus-pp-rec /recordings/room1234-user42-audio.mjr /output/user42-audio.opus # Video: MJR to WebM
janus-pp-rec /recordings/room1234-user42-video.mjr /output/user42-video.webm
# Audio: MJR to Opus
janus-pp-rec /recordings/room1234-user42-audio.mjr /output/user42-audio.opus # Video: MJR to WebM
janus-pp-rec /recordings/room1234-user42-video.mjr /output/user42-video.webm - VideoRoom: SFU room with publish/subscribe model
- AudioBridge: MCU audio mixing
- Streaming: for broadcasting pre-recorded or live streams
- Admin API: diagnostics, pcap dumps, live draining - webrtcup: media channel established. If this never arrives, look at NAT/firewall.
- media: Janus started (or stopped) receiving media. receiving: false means the publisher dropped.
- slowlink: packet loss detected via RTCP NACK. The nacks field tells you how bad. Act before the user complains.
- hangup: connection torn down. reason explains why (ICE failed, DTLS alert, close). - threads: number of forwarding threads for publisher-to-subscriber fan-out. Increase this when the plugin starts falling behind under heavy load.
- bitrate: enforced via RTCP REMB. Set it at room level, override individually for screen-share presenters who need more bandwidth. - Publisher connects to Janus A
- Your signaling server calls add_remote_publisher to project them into a room on Janus B
- Subscribers on Janus B see the remote publisher exactly like a local one - Network: UDP range for RTP (typically 10000-60000) must be reachable between clients and media server. Janus itself should not be externally reachable. Put a reverse proxy (nginx, HAProxy) in front, TLS-terminate on 443. Admin API: never exposed to the internet.
- TURN: Verify ICE/STUN/TURN reachability from every network segment your users will connect from. Set per-username allocation limits and bandwidth caps on your TURN server.
- Keepalive: Implement a robust loop with reconnect handling. Test with mobile clients, background tabs, and power-saving modes.
- Room params: notify_joining: false, publishers set to your actual speaker count, bitrate via REMB. Increase threads in VideoRoom config for heavy fan-out.
- Recording I/O: 10 publishers recording audio + video = 20 simultaneous write streams. NVMe handles it; spinning disk may not. Run post-processing on separate machines.
- File descriptors: ulimit -n 65536 before anything else.
- Load test: Not optional. Your first test will find something you missed. GStreamer with WebRTC support can simulate hundreds of fake clients. - handle_info: snapshot of WebRTC/ICE/DTLS/RTCP stats for a specific handle. Use it when one participant has frozen video or no audio.
- start_pcap / stop_pcap: targeted RTP dump for a single handle, opens in Wireshark. Invaluable for debugging individual participants without capturing everyone's traffic.
- accept_new_sessions: false: puts an instance in draining mode. Existing sessions finish, new joins go elsewhere. Use this before upgrades. - General Docs: architecture overview, installation, configuration
- REST API: sessions, attach, message, trickle, keepalive
- VideoRoom Plugin: SFU rooms, publish/subscribe, remote publishers, cascading
- AudioBridge Plugin: MCU audio mixing
- Admin/Monitor API: handle_info, pcap dumps, draining, Event Handlers
- Authentication: stored tokens, HMAC-signed tokens, api_secret
- Recording (MJR): MJR format, janus-pp-rec, mjr2pcap
- GitHub: source code, issues, demos