1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! 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 wraps [`rsipstack`] and owns the
//! wire-level concerns — SIP registration, dialogs, SDP offer/answer, and
//! RTP framing — while staying out of audio device I/O, codec work, and
//! call orchestration so it remains light and embeddable.
//!
//! [`rsipstack`]: https://crates.io/crates/rsipstack
//!
//! # Scope
//!
//! What this crate covers:
//!
//! - **SIP signaling** — REGISTER with digest auth and keepalive
//! re-registration ([`Registrar`]), shared transport + dialog layer
//! ([`SipEndpoint`]).
//! - **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`]) suitable for transcription, recording, or
//! smoke-testing inbound media, and a codec-agnostic send loop
//! ([`send_loop`]) that packetizes consumer-supplied payloads onto
//! the wire.
//!
//! Explicitly out of scope (push these to the consuming application):
//!
//! - Audio device I/O (e.g. `cpal`), codec encode/decode, jitter buffering,
//! recording, WAV writing.
//! - Account persistence (TOML files, system keychain).
//! - Call orchestration, AI pipeline, business logic.
//!
//! Inbound INVITE handling lives in [`Callee`]; outbound INVITEs are
//! placed via [`Caller`].
//!
//! # Quick start: register against a SIP server
//!
//! ```no_run
//! use std::sync::Arc;
//! use tokio_util::sync::CancellationToken;
//! use wavekat_sip::{Registrar, SipAccount, SipEndpoint, Transport};
//!
//! # async fn run() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! 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, _incoming) = SipEndpoint::new(&account, cancel.clone()).await?;
//! let endpoint = Arc::new(endpoint);
//!
//! // Expires: 60s, re-register every 50s.
//! let registrar = Registrar::new(account, endpoint, cancel, 60, 50)?;
//! registrar.register().await?;
//! registrar.keepalive_loop().await;
//! # Ok(())
//! # }
//! ```
//!
//! The `_incoming` stream returned by [`SipEndpoint::new`] yields inbound
//! transactions (INVITE, OPTIONS, …). For INVITEs, hand the transaction to
//! [`Callee::accept_transaction`] or [`Callee::reject_transaction`].
//!
//! # Placing an outbound call
//!
//! ```no_run
//! use std::sync::Arc;
//! use wavekat_sip::{Caller, SipAccount, SipEndpoint};
//!
//! # async fn run(account: SipAccount, endpoint: Arc<SipEndpoint>)
//! # -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! let caller = Caller::new(account, endpoint);
//! let target: wavekat_sip::re_exports::Uri = "sip:bob@example.com".try_into()?;
//! let mut pending = caller.dial(target).await?;
//!
//! // Pump pending.state_rx to render "Dialing…" / "Ringing…" in the UI.
//! // When you see DialogState::Confirmed, promote to AcceptedDial:
//! let accepted = pending.on_confirmed().await?;
//!
//! // Wire RTP to your audio / AI pipeline using accepted.rtp_socket
//! // and accepted.remote_media. Hang up locally with:
//! accepted.dialog.bye().await?;
//! # Ok(())
//! # }
//! ```
//!
//! # 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);
//!
//! // ... send `offer` in an INVITE, receive an SDP answer ...
//! 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
//!
//! For real receive loops use [`receive_rtp`]; to inspect individual
//! packets, parse directly:
//!
//! ```
//! 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, dialog layer, RX stream. |
//! | [`registrar`] | REGISTER + digest auth + keepalive re-registration. |
//! | [`callee`] | Inbound INVITE accept/reject helper. |
//! | [`caller`] | Outbound INVITE / dial-cancel helper. |
//! | [`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. Pin
//! an exact version if you need stability today.
//!
//! # License
//!
//! Licensed under Apache 2.0. Copyright 2026 WaveKat.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
/// Re-exports of upstream types that appear in our public API. Pinning
/// them here lets consumers depend only on `wavekat-sip` without taking
/// a direct dep on `rsip` / `rsipstack`.
/// Short git hash this crate was built from, or `"unknown"` if unavailable.
pub const GIT_HASH: &str = env!;