rtsp-runtime
Sans-IO RTSP 1.0 (RFC 2326) session engine: a driveable client and a server state machine, Transport negotiation, interleaved RTP/RTCP framing and authenticated requests. An optional tokio adapter (and tls for rtsps://) puts it on real sockets.
[]
= "0.8" # sans-IO core
= { = "0.8", = ["tokio"] } # + real sockets
= { = "0.8", = ["tls"] } # + rtsps:// (TLS)
What it does / what it does not
Does: run the RFC 2326 Appendix A session state machines (Init, Ready, Playing, Recording) on both sides; build outbound requests (OPTIONS, DESCRIBE, SETUP, PLAY, PAUSE, TEARDOWN, ANNOUNCE, RECORD, GET_PARAMETER) with an incrementing CSeq and match responses back to them; parse and serialize the Transport (§12.39) and Session (§12.37) headers; frame and de-frame $-channel interleaved RTP/RTCP (§10.12); answer 401 challenges with Basic, Digest or Bearer credentials, including stale=true nonce refresh; and manage the keepalive timeout. The server rejects a method that is illegal in the current state with 455, allocates a Session id on SETUP (from a caller-supplied CSPRNG), and echoes the first offered transport.
Does not: open sockets in the core (feed bytes in, get bytes and events out), parse RTP or RTCP payloads (use rtp-packet and rtcp-packet), parse SDP (use sdp-types), generate DESCRIBE SDP or challenge clients on the server side (the server session has no authentication; put a broadcast-auth Verifier in your own layer), or speak RTSP 2.0 (RFC 7826 is vendored but not implemented). The client has no SET_PARAMETER builder, and server-initiated REDIRECT is not modelled.
Typical users: IP-camera ingest (multimux's RTSP input), RTSP push to a media server, test servers, and tools that need to drive a camera session deterministically.
Standards supported
| Standard | Edition | Scope | Status | Notes |
|---|---|---|---|---|
| IETF RFC 2326 "Real Time Streaming Protocol" | RFC 2326 (vendored specs/rfc2326_rtsp.txt, transcribed in rtsp-runtime/docs/rfc2326.md) |
Appendix A state machines, §10 methods listed above, §10.12 interleaved framing, §12.37 Session, §12.39 Transport, §14 authentication, CSeq (§12.17) |
Partial | See the methods table below |
| IETF RFC 7826 "RTSP 2.0" | RFC 7826 (vendored, not implemented) | n/a | Not implemented | |
| IETF RFC 7617 / RFC 7616 / RFC 6750 (Basic, Digest, Bearer) | not vendored, unverified here | Client side via broadcast-auth |
Full | Server side has no authentication |
| IETF RFC 8866 (SDP) | RFC 8866 (vendored) | Bodies carried opaque | Typed-opaque | Parse with sdp-types |
| IETF RFC 3550 (RTP/RTCP) | RFC 3550 | Interleaved payload is carried as bytes | Typed-opaque | Decode with rtp-packet / rtcp-packet |
rtsps:// (RTSP over TLS, default port 322) |
n/a | Client connect_tls, server accept_tls |
Full, behind tls |
webpki-roots trust by default or a custom rustls::ClientConfig |
| Method | Client builds | Server session handles | State effect |
|---|---|---|---|
| OPTIONS, DESCRIBE, GET_PARAMETER, ANNOUNCE | yes | accepted, empty 200 | none |
| SET_PARAMETER | no builder | accepted as state-neutral, empty 200 | none |
| SETUP | yes | allocates Session, echoes transport |
Init to Ready |
| PLAY, PAUSE, RECORD, TEARDOWN | yes | accepted, requires Session |
Ready/Playing/Recording per Appendix A |
| REDIRECT | no | not modelled (a client resets to Init on a 3xx) |
n/a |
Features
| Feature | Default | Description |
|---|---|---|
serde |
no | Serialize on the public wire and event types. |
tokio |
no | AsyncRtspClient / AsyncRtspServer (tokio_util Framed adapters; 64 KiB header and 2 MiB message caps; RtspTimeouts; keepalive at half the session timeout). |
tls |
no | Implies tokio; adds rtsps:// via tokio-rustls. |
The core is std and uses no async runtime. The crate has #![forbid(unsafe_code)].
Quick start
A client talking to a server entirely in memory (no socket):
use ;
let url = "rtsp://127.0.0.1/stream";
let track = "rtsp://127.0.0.1/stream/trackID=0";
// A fixed id is fine here; a real server must draw ids from a CSPRNG.
let mut server = new;
let mut client = new;
let req = client.options.unwrap;
let = server.handle_request.unwrap;
client.handle_data_strict.unwrap;
let transport = single;
let req = client.setup.unwrap;
let = server.handle_request.unwrap;
client.handle_data_strict.unwrap;
assert_eq!;
assert!;
let req = client.play.unwrap;
let = server.handle_request.unwrap;
client.handle_data_strict.unwrap;
assert_eq!;
// While playing, RTP is tunnelled on interleaved channel 0 over the same connection.
let rtp = ;
let wire = slice_to_bytes.unwrap;
let events = client.handle_data_strict.unwrap;
assert!;
let req = client.teardown.unwrap;
let = server.handle_request.unwrap;
client.handle_data_strict.unwrap;
assert_eq!;
Runnable programs: client_server_loopback and transport_and_framing (cargo run -p rtsp-runtime --example <name>).
What is implemented
ClientSession: request builders,handle_data(returnsHandleOutcome { events, error }so frames decoded before a terminal error are never lost) andhandle_data_strict;with_credentials;AuthRetryevents for401;poll_timeout/handle_timeoutkeepalive.ServerSession:handle_requestreturning response bytes plusServerEvents (RequestAccepted,MethodNotValid,SessionSetup,MediaData),with_session_timeout.Transport,TransportSpec,LowerTransport,Delivery,TransportMode: UDP unicast and multicast, TCP interleaved.SessionHeader:Sessionid andtimeout. Both are parsed by this crate over one RFC 2326 §15.1 lexer, becausertsp-types0.1 is exact-case and drops case-variant parameters (a documented, owner-approved exception to the no-hand-roll rule; probe table indocs/transport-header.md).InterleavedFrameand a streaming demultiplexer (complete frames plus a partial tail).CredentialsandAuthenticator, re-exported frombroadcast-auth.io(featuretokio):AsyncRtspClient,AsyncRtspServer,RtspTimeouts,RTSP_DEFAULT_PORT(554),RTSPS_DEFAULT_PORT(322).
Design notes
Sans-IO: the core never reads a clock or a socket (the caller passes Instant for timeouts and supplies the session-id source). Error is #[non_exhaustive]. The Session id comparison is constant time. Message framing is bounded (header and message size caps). Tests include golden wire transcripts and real-loopback integration tests (OPTIONS to TEARDOWN, fragmented interleaved media, Digest, a TLS handshake with a self-signed certificate). A lexical no_handroll_guard test enforces the allowlist.
Related crates
Builds on rtsp-types and sdp-types (codecs) and broadcast-auth. Pair with rtp-packet and rtcp-packet for the interleaved payloads. multimux uses it for RTSP input and push, and transmux for RTP depacketising.
MSRV and licence
MSRV 1.95.0. Licensed under MIT OR Apache-2.0.