Skip to content
zyno-ioPublic

About

High-performance RTP media routing server in Rust. Routes audio between WebRTC, RTP/SRTP, and file playback endpoints with codec transcoding (PCMU, G.722, Opus), DTMF detection/injection, PCAP recording, and voice activity detection. Controlled via WebSocket JSON API. Designed for telephony infrastructure and SIP integration.

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

rtpbridge

A high-performance RTP media routing server in Rust, loosely inspired by rtpengine.

rtpbridge sits between VoIP endpoints, routing audio between them with support for WebRTC, plain RTP/SRTP, codec transcoding, DTMF, recording, voice activity detection, and file playback. It is controlled via a WebSocket JSON interface where each connection maps 1:1 to a media session.

Features

  • WebRTC endpoints via str0m (DTLS, ICE lite, SRTP)
  • Plain RTP/SRTP endpoints with SDES key exchange
  • Codec transcoding between PCMU (G.711), G.722, and Opus
  • DTMF detection, forwarding, and injection (RFC 4733 telephone-event)
  • PCAP recording of decrypted media (RTP + RTCP) with accurate timestamps
  • Voice Activity Detection via earshot (independent of recording)
  • File playback from local files or URLs (WAV, MP3, OGG, FLAC) with seek, pause, loop
  • Cross-session endpoint transfer — move endpoints between sessions with zero overt media disruption (endpoint.transfer)
  • Session bridging — bidirectional audio bridge between sessions using decoded PCM (L16 at 48kHz) with no lossy re-encoding at the bridge boundary (session.bridge)
  • Session management with orphan timeout, reconnection (session.attach), and empty session auto-destroy
  • Graceful shutdown with configurable drain wait (k8s compatible)
  • Split interface binding for separate control and media networks
  • Native TLS/WSS control listener on the existing control port, with optional HMAC authorization for privileged control operations
  • Symmetric RTP with first-packet tuple latching for NAT traversal

Quick Start

Build

# Requires Rust 1.94+, libopus headers, a C compiler, make, Perl and curl
sh scripts/build-openssl.sh target/openssl
export OPENSSL_DIR="$PWD/target/openssl" OPENSSL_STATIC=1
cargo build --release

Run

# Default: WebSocket on 127.0.0.1:9100, media on 127.0.0.1
./target/release/rtpbridge --listen 127.0.0.1:9100 --media-ip 203.0.113.5

# With config file
./target/release/rtpbridge --config rtpbridge.toml

Non-loopback control listeners require TLS and HMAC configuration. A trusted TLS proxy may use allow_plaintext_control = true on its protected upstream; unauthenticated remote control requires the separate explicit development exception allow_unauthenticated_control = true. The HMAC key authorizes all sessions.

Connect

Connect a WebSocket client and send JSON messages:

# Using websocat
websocat ws://localhost:9100
{"id":"1","method":"session.create","params":{}}

Configuration

CLI Arguments

Argument Default Description
--listen 127.0.0.1:9100 WebSocket/HTTP control plane address(es), comma-separated
--media-ip 127.0.0.1 IP for all media sockets (RTP/WebRTC)
--config — Path to TOML config file
--log-level info Log level (trace/debug/info/warn/error)

TOML Configuration

listen = "127.0.0.1:9100"
media_ip = "203.0.113.5"            # or "203.0.113.5, 2001:db8::5" for dual-stack
rtp_port_range = [30000, 39999]
disconnect_timeout_secs = 30
shutdown_max_wait_secs = 300
max_sessions = 10000
max_endpoints_per_session = 20
max_recordings_per_session = 100
session_idle_timeout_secs = 0     # 0 = disabled
recording_dir = "/var/lib/rtpbridge/recordings"
media_dir = "/var/lib/rtpbridge/media"
cache_dir = "/tmp/rtpbridge-cache"
cache_cleanup_interval_secs = 300
ws_ping_interval_secs = 30
log_level = "info"
# See rtpbridge.toml.example for all available options

Control-plane security

Set [tls] to serve HTTPS/WSS on the existing listen port; rtpbridge does not mix plaintext and TLS on that port. Set auth_hmac_secret_file to require short-lived HMAC-SHA256 signatures for control WebSocket upgrades and sensitive HTTP routes. /audio/<connect_token> remains a separate single-use audio capability, so AI/media consumers do not receive the control signing key. See the configuration guide for the exact configuration and wire format.

Control Protocol

All communication uses JSON over WebSocket. Each connection is bound to exactly one session.

The control API is intended for backend clients. Without HMAC, privileged requests carrying a browser Origin are rejected, and loopback clients must use a loopback IP or localhost in Host. This protects local development listeners from cross-site requests and DNS rebinding. Browser audio uses the separate /audio/<connect_token> capability.

Methods

Method Description
session.create Create a new session
session.attach Reattach to an orphaned session
session.destroy Destroy the current session
session.info Get session details
session.list List all sessions on the server
server.info Get server hostname and configured media IP
endpoint.create_from_offer Create endpoint from remote SDP offer (auto-detects WebRTC vs RTP)
endpoint.create_offer Create a new endpoint and generate SDP offer
endpoint.accept_answer Accept remote SDP answer
endpoint.accept_offer Accept remote SDP offer for an existing endpoint
endpoint.create_with_file Create file playback endpoint
endpoint.create_tone Create generated tone endpoint
endpoint.create_websocket Create raw PCM WebSocket audio endpoint
endpoint.remove Remove an endpoint
endpoint.ice_restart ICE restart (WebRTC only)
endpoint.update_direction Override or restore endpoint routing direction
endpoint.update_remote_sdp Update remote RTP address/SRTP state without codec changes
endpoint.file.seek Seek file playback position
endpoint.file.pause Pause file playback
endpoint.file.resume Resume file playback
endpoint.dtmf.inject Inject DTMF digit into endpoint
endpoint.dtmf.set_sensitive Redact DTMF logs, tag control events, and omit DTMF packets from recordings for an endpoint
endpoint.srtp_rekey Initiate SRTP rekey (plain RTP/SRTP only)
endpoint.transfer Transfer an endpoint to a different session
session.bridge Bidirectional audio bridge between two sessions
recording.start Start recording (full session or single leg)
recording.stop Stop recording
vad.start Start voice activity detection on endpoint
vad.stop Stop voice activity detection
fax_detect.start Start fax tone detection (CNG/CED) on endpoint
fax_detect.stop Stop fax tone detection
stats.subscribe Subscribe to periodic session statistics
stats.unsubscribe Unsubscribe from statistics

recording.start, recording.stop, endpoint removal, and the file-playback lifecycle events carry media-host epoch milliseconds. These use the same host clock as PCAP packet records, so callers can safely align explicitly injected media to a recording without comparing control-plane clocks. endpoint.file.started is emitted only immediately before the first file RTP is routed and carries that same epoch; ledger consumers must use it rather than endpoint creation, which can remain buffering. pcap2audio --metadata <path> writes its earliest decodable-RTP sample-zero epoch and rendered WAV duration as JSON for conversion services.

Events

Event Description
dtmf DTMF digit detected from remote endpoint
endpoint.state_changed Endpoint state transition
endpoint.ice_state_changed WebRTC ICE state transition
endpoint.file.started First file RTP packet entered media routing
endpoint.file.finished File playback completed
endpoint.tone.finished Tone generation completed
endpoint.ws.connected WebSocket audio socket attached
endpoint.ws.disconnected WebSocket audio socket closed
endpoint.ws.connect_timeout WebSocket audio endpoint was not dialed in before timeout
endpoint.media_timeout No media received from remote endpoint
endpoint.rtcp_bye RTCP BYE received from remote endpoint
endpoint.transferred_out Endpoint transferred to another session
endpoint.transferred_in Endpoint transferred in from another session
events.dropped Events dropped due to client backpressure
recording.stopped Recording stopped externally
session.idle_timeout Session destroyed due to inactivity
session.empty_timeout Session destroyed due to zero endpoints timeout
session.orphaned Control connection dropped, timeout running
stats Periodic session statistics
vad.speech_started Speech detected after silence
vad.silence Periodic silence notification
vad.error VAD analysis decoder could not be created
fax.cng_detected Fax calling tone (CNG, 1100 Hz) detected
fax.ced_detected Fax/modem answer tone (CED, 2100 Hz) detected
fax.error Fax analysis decoder could not be created

See Events Reference for detailed payload schemas.

HTTP REST API

rtpbridge also serves HTTP endpoints on the same listen address(es). See Configuration — HTTP REST API for full details including status codes and pagination.

Endpoint Method Description
/health GET Health check — returns {"status":"ok"}
/metrics GET Prometheus-format metrics
/sessions GET List all active sessions
/sessions/{id} GET Get session details
/recordings GET List PCAP recording files
/recordings/{path} GET Download a recording file
/recordings/{path} DELETE Delete a recording file

Architecture

                    WebSocket Control (JSON)
                           |
                    +------v------+
                    | SessionMgr  |  DashMap<SessionId, Session>
                    +------+------+
                           |
              +------------v------------+
              |     Session Task        |  (one tokio task per session)
              |                         |
              |  +--------+ +--------+  |
              |  | WebRTC | |  RTP/  |  |
              |  |  str0m | | SRTP   |  |  +--------+  +----------+
              |  +---+----+ +---+----+  |  |  File   |  | Recording|
              |      |          |       |  |Playback |  |  (PCAP)  |
              |      +----+-----+       |  +----+----+  +----------+
              |           |             |       |
              |    Routing Table        |       |
              |  (sendrecv/recvonly/    |       |
              |   sendonly directions)  |       |
              +-------------------------+-------+
                           |
              +------------v------------+
              |    Per-Endpoint UDP     |
              |    Sockets              |
              +-------------------------+

Testing

# Unit tests
cargo test

# Integration tests (serial — spawns server processes)
cargo test -- --test-threads=1

License

MIT

Links

About

High-performance RTP media routing server in Rust. Routes audio between WebRTC, RTP/SRTP, and file playback endpoints with codec transcoding (PCMU, G.722, Opus), DTMF detection/injection, PCAP recording, and voice activity detection. Controlled via WebSocket JSON API. Designed for telephony infrastructure and SIP integration.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages