Expand description
SIP signaling and RTP transport for voice pipelines.
wavekat-sip is a small, focused toolkit for building softphones, voice
bots, and recording bridges in Rust. It owns the wire-level concerns — SIP
registration, dialogs, SDP offer/answer, and RTP framing — on a from-scratch
engine (no external SIP stack), while staying out of audio device I/O, codec
work, and call orchestration so it remains light and embeddable.
The SIP transaction/dialog/transport engine is built in-house (see the
stack plan in docs/08-own-sip-stack.md); only the rsip crate is used,
for SIP message types. The engine is an internal detail — consumers depend on
wavekat-sip alone.
§Scope
What this crate covers:
- SIP signaling — REGISTER with digest auth and keepalive
re-registration (
Registrar), a bound endpoint with inbound-call routing (SipEndpoint), outbound calls (Caller), and inbound calls (IncomingCall). - SDP — minimal G.711 (PCMU + PCMA) offer/answer with round-trip
parsing (
build_sdp,parse_sdp). - RTP — header parser (
RtpHeader), a debug-friendly receive loop (receive_rtp), and a codec-agnostic send loop (send_loop).
Explicitly out of scope (push these to the consuming application): audio device I/O, codec encode/decode, jitter buffering, recording; account persistence; call orchestration / AI pipeline / business logic.
§Quick start: register against a SIP server
use tokio_util::sync::CancellationToken;
use wavekat_sip::{Registrar, SipAccount, SipEndpoint, Transport};
let account = SipAccount {
display_name: "Office".into(),
username: "1001".into(),
password: "secret".into(),
domain: "sip.example.com".into(),
auth_username: None,
server: None,
port: None,
transport: Transport::Udp,
};
let cancel = CancellationToken::new();
let endpoint = SipEndpoint::new(&account, cancel.clone()).await?;
// Expires: 60s, re-register every 50s.
let registrar = Registrar::new(account, endpoint, cancel, 60, 50)?;
registrar.register().await?;
registrar.keepalive_loop().await;§Placing an outbound call
use std::sync::Arc;
use wavekat_sip::{Caller, SipAccount, SipEndpoint};
let caller = Caller::new(account, endpoint);
let target: wavekat_sip::re_exports::Uri = "sip:bob@example.com".try_into()?;
let mut call = caller.dial(target).await?;
// Wire RTP to your audio / AI pipeline using call.rtp_socket and
// call.remote_media. Hang up locally with:
call.hangup().await?;§Answering inbound calls
use std::sync::Arc;
use wavekat_sip::SipEndpoint;
while let Some(incoming) = endpoint.next_incoming_call().await {
// Inspect incoming.remote_media, then accept (or reject):
let _call = incoming.accept().await?;
}§Building SDP and parsing the answer
use std::net::{IpAddr, Ipv4Addr};
use wavekat_sip::{build_sdp, parse_sdp};
let local_ip: IpAddr = Ipv4Addr::new(192, 168, 1, 50).into();
let offer = build_sdp(local_ip, 20000);
let answer = offer.clone(); // simulate a loopback answer
let media = parse_sdp(&answer).expect("valid SDP");
assert_eq!(media.port, 20000);
assert_eq!(media.payload_type, 0); // PCMU§Reading RTP headers off the wire
use wavekat_sip::RtpHeader;
let packet = [
0x80, 0x00, 0x04, 0xD2, // V=2, PT=0 (PCMU), seq=1234
0x00, 0x00, 0x16, 0x2E, // timestamp
0xDE, 0xAD, 0xBE, 0xEF, // SSRC
];
let header = RtpHeader::parse(&packet).unwrap();
assert_eq!(header.payload_type, 0);
assert_eq!(header.sequence, 1234);
assert_eq!(header.header_len(), 12);§Module map
| Module | Purpose |
|---|---|
account | Runtime SipAccount + Transport enum. |
endpoint | SipEndpoint — bound transport, engine, inbound-call routing. |
registrar | REGISTER + digest auth + keepalive re-registration. |
resolve | RFC 3263 (subset) server location: SRV + A/AAAA fallback. |
callee | IncomingCall — inbound INVITE accept/reject. |
caller | Caller outbound dial + the Call handle. |
sdp | Minimal G.711 offer/answer build + parse. |
rtp | RTP header parser, debug receive loop, codec-agnostic send loop. |
§Stability
Pre-1.0. The public API may still shift between minor versions.
§License
Licensed under Apache 2.0. Copyright 2026 WaveKat.
Re-exports§
pub use account::SipAccount;pub use account::Transport;pub use callee::IncomingCall;pub use caller::Call;pub use caller::CallSession;pub use caller::Caller;pub use caller::InboundRequests;pub use dtmf_info::build_info_body;pub use dtmf_info::content_type_header;pub use dtmf_info::InfoOutcome;pub use endpoint::SipEndpoint;pub use inbound::InboundRequest;pub use refer::is_final_sipfrag;pub use refer::parse_sipfrag_status;pub use refer::refer_to_header;pub use registrar::Registrar;pub use registrar::RegistrarDiagnostics;pub use resolve::order_candidates;pub use resolve::resolve_sip_server;pub use resolve::SrvRecord;pub use rtp::dtmf::build_event_payload;pub use rtp::dtmf::build_rtp_dtmf_packet;pub use rtp::dtmf::send_dtmf_burst;pub use rtp::dtmf::DtmfBurstConfig;pub use rtp::dtmf::DtmfDigit;pub use rtp::dtmf::DEFAULT_VOLUME_DBM0;pub use rtp::dtmf_recv::parse_event_payload;pub use rtp::dtmf_recv::DtmfEvent;pub use rtp::dtmf_recv::DtmfEventPayload;pub use rtp::dtmf_recv::DtmfReceiver;pub use rtp::receive_rtp;pub use rtp::send_loop;pub use rtp::RtpHeader;pub use rtp::RtpSendConfig;pub use sdp::build_sdp;pub use sdp::build_sdp_with;pub use sdp::parse_sdp;pub use sdp::MediaDirection;pub use sdp::RemoteMedia;pub use sdp::DTMF_DEFAULT_PT;pub use session_timer::min_se_in;pub use session_timer::negotiate_uac;pub use session_timer::negotiate_uas;pub use session_timer::require_timer_header;pub use session_timer::session_expires_in;pub use session_timer::session_timer_loop;pub use session_timer::supported_timer_header;pub use session_timer::supports_timer;pub use session_timer::Refresher;pub use session_timer::SessionDialogOps;pub use session_timer::SessionExpires;pub use session_timer::SessionTimer;pub use session_timer::SessionTimerOutcome;pub use session_timer::UasSessionTimer;pub use session_timer::DEFAULT_SESSION_EXPIRES_SECS;pub use session_timer::MIN_SESSION_EXPIRES_SECS;
Modules§
- account
- Runtime SIP account configuration.
- callee
- Inbound calls: accept with an SDP answer, or reject.
- caller
- Outbound calls and the established-call handle.
- dtmf_
info - SIP INFO fallback transport for DTMF (
application/dtmf-relay). - endpoint
- Shared SIP endpoint: a bound UDP transport + the clean-room engine, with inbound requests routed to new calls or auto-answered in-dialog.
- inbound
- Inbound in-dialog requests surfaced to the consumer.
- re_
exports - Re-exports of the
rsipmessage types that appear in our public API. Pinning them here lets consumers depend only onwavekat-sip. - refer
- Call transfer primitives —
REFER(RFC 3515) for blind transfer. - registrar
- REGISTER + digest auth + keepalive re-registration, over the engine.
- resolve
- SIP server location per a subset of RFC 3263: DNS SRV lookup with RFC 2782 ordering, falling back to plain A/AAAA.
- rtp
- RTP header parsing, a debug receive loop, a codec-agnostic send loop,
and RFC 4733 DTMF (
telephone-event) in both directions. - sdp
- Minimal SDP offer/answer for G.711 telephony audio plus RFC 4733
telephone-event(DTMF) negotiation. - session_
timer - RFC 4028 session timers — keep long calls from outliving a dead dialog.
Constants§
- GIT_
HASH - Short git hash this crate was built from, or
"unknown"if unavailable.