Skip to content

Commit 524dce6

Browse files
committed
replace with crate::utils::spawn[latest]
1 parent 0f77fd9 commit 524dce6

69 files changed

Lines changed: 657 additions & 284 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config.toml.example

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,30 +102,43 @@ type = "extension"
102102
# are stripped automatically. Defaults to ["H264"] when not set.
103103
# video_codecs = ["H264"]
104104

105-
[recording]
106-
enabled = true
107-
auto_start = true
108-
109-
# When [sipflow] is enabled, SipFlow owns RTP recording and this live recorder is disabled.
110-
# Configure [sipflow.upload] below when SipFlow recordings should be uploaded after calls.
105+
# ── Recording ─────────────────────────────────────────────────────────────────
106+
# IMPORTANT: [recording] and [sipflow] are mutually exclusive for RTP capture.
107+
#
108+
# • If [recording] (top-level) or [proxy.recording] is configured, the legacy
109+
# live WAV recorder owns RTP recording. SipFlow will still capture SIP
110+
# signalling messages, but RTP audio capture is DISABLED.
111+
# • If you omit [recording] entirely, SipFlow captures both SIP signalling
112+
# and RTP audio. This is the recommended default.
113+
#
114+
# Uncomment below ONLY when you need the legacy WAV recorder instead of SipFlow.
115+
# [recording]
116+
# enabled = true
117+
# auto_start = true
118+
# # Recording upload mode: "local" (default), "http", or "s3".
119+
# # type = "local"
120+
# # path = "./recordings"
111121

112122
[callrecord]
113123
type = "local"
114124
root = "./config/cdr"
115125

116-
# SipFlow: Unified SIP+RTP recording system
117-
# [sipflow]
118-
# type = "local"
119-
# root = "./config/sipflow"
120-
# subdirs = "daily" # "none" | "daily" | "hourly"
126+
# ── SipFlow ───────────────────────────────────────────────────────────────────
127+
# Unified SIP + RTP recording system (default).
128+
# When enabled WITHOUT a [recording] section, SipFlow captures both SIP
129+
# signalling and RTP audio. The console shows the SIP flow ladder and an
130+
# audio player for each call.
131+
[sipflow]
132+
type = "local"
133+
root = "./config/sipflow"
134+
subdirs = "daily" # "none" | "daily" | "hourly"
121135

122-
# Local: SQLite + structured file (default settings)
123-
# type = "local"
124-
# root = "./config/sipflow"
136+
# Advanced local backend settings (defaults shown):
125137
# flush_count = 1000
126138
# flush_interval_secs = 5
127139
# id_cache_size = 8192
128-
#
140+
141+
# Optional: upload captured media as WAV after each call.
129142
# [sipflow.upload]
130143
# type = "s3"
131144
# vendor = "aws"
@@ -136,15 +149,15 @@ root = "./config/cdr"
136149
# endpoint = "https://s3.amazonaws.com"
137150
# root = "recordings"
138151

139-
# Remote (single node, legacy format)
152+
# Remote backend (single node):
153+
# [sipflow]
140154
# type = "remote"
141155
# udp_addr = "192.168.1.100:3000"
142156
# http_addr = "http://192.168.1.100:3001"
143157
# timeout_secs = 10
144158

145-
# Remote cluster (multi-node, consistent hashing by call_id)
146-
# Any proxy node can write → each call_id is hashed to exactly one server.
147-
# Any proxy node can read → same hash routes the query to the right server.
159+
# Remote cluster (multi-node, consistent hashing by call_id):
160+
# [sipflow]
148161
# type = "remote"
149162
# timeout_secs = 10
150163
# nodes = [

docs/config/06-media-recording.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ codecs = ["opus", "pcmu", "pcma", "g729"]
1717
```
1818

1919
## Recording Policy
20+
21+
> **[recording] and [sipflow] are mutually exclusive for RTP capture.**
22+
> The default configuration uses `[sipflow]` for both SIP signalling and RTP
23+
> audio capture. Only configure `[recording]` when you specifically need the
24+
> legacy live WAV recorder. See [08-sipflow.md](08-sipflow.md) for details.
25+
2026
Control when calls are recorded. Can be set at top-level `[recording]` or per-proxy `[proxy.recording]` (proxy-level overrides top-level).
2127

2228
`[recording]` controls the live WAV recorder. When enabled, the recorder always writes a local WAV first. Set `type = "http"` or `type = "s3"` only to upload that local WAV after the call completes.
@@ -28,7 +34,7 @@ Recording configuration has priority over SipFlow RTP recording. If a top-level
2834
- SipFlow SIP message capture still works when `[sipflow]` is enabled.
2935
- SipFlow RTP capture and `[sipflow.upload]` recording export are disabled for that call.
3036

31-
Only omit the recording section entirely when you want `[sipflow.upload]` to act as the recording source.
37+
Only omit the recording section entirely when you want SipFlow to capture RTP audio and/or `[sipflow.upload]` to act as the recording source.
3238

3339
```toml
3440
# Top-level recording config (applies to all proxies unless overridden)

docs/config/08-sipflow.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
SipFlow is the SIP flow capture subsystem built into RustPBX. It captures call SIP signalling messages into a compact on-disk store and can also capture bi-directional RTP media when no explicit recording policy is configured. The console and HTTP API use this data to query call flows and, when media capture is enabled, replay recordings by Call-ID.
44

5+
> **Default configuration.** The example config (`config.toml.example`) enables
6+
> `[sipflow]` with `type = "local"` and omits `[recording]`. This gives you SIP
7+
> signalling capture **and** RTP audio capture out of the box. If you add a
8+
> `[recording]` section, SipFlow will stop capturing RTP audio (signalling
9+
> capture continues). See the "Recording Priority" section below.
10+
511
See [docs/sipflow.md](../sipflow.md) for the full deployment guide (architecture diagrams, systemd unit, storage sizing, and API reference).
612

713
---
@@ -70,6 +76,11 @@ Start the standalone server:
7076

7177
## Recording Priority
7278

79+
> **WARNING: `[recording]` and `[sipflow]` are mutually exclusive for RTP
80+
> capture.** If you configure both, SipFlow will capture SIP signalling but
81+
> **NOT** RTP audio. To get recordings in the console audio player, either
82+
> remove `[recording]` or ensure `[recording] enabled = true` writes WAV files.
83+
7384
SipFlow SIP message capture is independent from call recording. If `[sipflow]` is enabled, RustPBX continues to store SIP messages for the caller and callee Call-IDs.
7485

7586
RTP media capture follows the recording policy:
@@ -115,13 +126,16 @@ url = "https://archive.example.com/upload"
115126
SipFlow capture is enabled by configuring the `[sipflow]` backend. It is independent from `[callrecord]`, which only controls CDR JSON storage.
116127

117128
```toml
129+
# Default configuration (recommended):
130+
# - SipFlow captures SIP signalling AND RTP audio
131+
# - No [recording] section needed
118132
[sipflow]
119133
type = "local"
120134
root = "./config/sipflow"
121135
subdirs = "daily"
122136
```
123137

124-
When SipFlow is active, each CDR entry in the console can display a **SIP Flow** tab with the signalling ladder. The audio player is available only when the active recording policy produced media, either through `[recording]` or through SipFlow RTP capture when no recording section exists.
138+
When SipFlow is active and **no `[recording]` section exists**, each CDR entry in the console shows both the **SIP Flow** tab (signalling ladder) and the **audio player** (recorded RTP). If a `[recording]` section is present, the audio player relies on the legacy recorder's WAV output instead.
125139

126140
---
127141

examples/rwi_cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ async fn main() -> anyhow::Result<()> {
5353
let (mut write, mut read) = ws.split();
5454

5555
// Spawn task to read events
56-
let read_task = tokio::spawn(async move {
56+
let read_task = rustpbx::utils::spawn(async move {
5757
while let Some(msg) = read.next().await {
5858
match msg {
5959
Ok(Message::Text(text)) => {

src/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ impl AppStateBuilder {
511511
}
512512

513513
if let Some(mut manager) = callrecord_manager {
514-
tokio::spawn(async move {
514+
crate::utils::spawn(async move {
515515
manager.serve().await;
516516
});
517517
}

src/bin/sipflow.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ async fn main() -> Result<()> {
8181
let (tx, mut rx) = tokio::sync::mpsc::channel::<Packet>(args.buffer_size);
8282

8383
// UDP Receiver Task
84-
tokio::spawn(async move {
84+
rustpbx::utils::spawn(async move {
8585
let mut buf = vec![0u8; 65535];
8686
loop {
8787
match socket.recv_from(&mut buf).await {
@@ -99,7 +99,7 @@ async fn main() -> Result<()> {
9999

100100
// Storage Worker Task
101101
let storage_worker = storage.clone();
102-
tokio::spawn(async move {
102+
rustpbx::utils::spawn(async move {
103103
let mut interval = tokio::time::interval(std::time::Duration::from_secs(1));
104104
loop {
105105
tokio::select! {

src/call/app/controller.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ impl CallController {
228228
let tx = self.fired_timer_tx.clone();
229229
let cancelled = self.cancelled_timers.clone();
230230
let id_task = id.clone();
231-
tokio::spawn(async move {
231+
crate::utils::spawn(async move {
232232
tokio::time::sleep(delay).await;
233233
// Only fire if not cancelled in the meantime.
234234
let was_cancelled = cancelled.lock().unwrap().remove(&id_task);
@@ -452,7 +452,7 @@ mod tests {
452452

453453
// Spawn a task that monitors commands and sends RecordingComplete when StopRecording is received
454454
let event_tx_clone = event_tx.clone();
455-
tokio::spawn(async move {
455+
crate::utils::spawn(async move {
456456
while let Some(cmd) = cmd_rx.recv().await {
457457
if matches!(cmd, CallCommand::StopRecording) {
458458
// Simulate the session processing the stop and sending back RecordingComplete
@@ -480,7 +480,7 @@ mod tests {
480480
let (mut controller, event_tx, mut cmd_rx) = make_controller_with_channels();
481481

482482
// Spawn a task that sends Hangup instead of RecordingComplete
483-
tokio::spawn(async move {
483+
crate::utils::spawn(async move {
484484
// Wait for StopRecording command
485485
while let Some(cmd) = cmd_rx.recv().await {
486486
if matches!(cmd, CallCommand::StopRecording) {
@@ -503,7 +503,7 @@ mod tests {
503503
let (mut controller, event_tx, mut cmd_rx) = make_controller_with_channels();
504504

505505
let event_tx_clone = event_tx.clone();
506-
tokio::spawn(async move {
506+
crate::utils::spawn(async move {
507507
// Wait for StopRecording command
508508
while let Some(cmd) = cmd_rx.recv().await {
509509
if matches!(cmd, CallCommand::StopRecording) {

src/call/app/ivr/executor.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl StepIvrApp {
101101
fn record_trace(&self, entry: IvrTraceEntry) {
102102
if let Some(t) = self.effective_trace() {
103103
let ent = entry.clone();
104-
tokio::spawn(async move {
104+
crate::utils::spawn(async move {
105105
t.record_entry(ent).await;
106106
});
107107
}
@@ -123,7 +123,7 @@ impl StepIvrApp {
123123
error: entry.error.clone(),
124124
};
125125
let gw = gw.clone();
126-
tokio::spawn(async move {
126+
crate::utils::spawn(async move {
127127
let guard = gw.read();
128128
guard.fan_out_event_to_context(&call_id, &event, &call_id);
129129
});
@@ -138,7 +138,7 @@ impl StepIvrApp {
138138
.get("session_id")
139139
.cloned()
140140
.unwrap_or_default();
141-
tokio::spawn(async move {
141+
crate::utils::spawn(async move {
142142
t.increment_steps(&sid).await;
143143
});
144144
}
@@ -163,7 +163,7 @@ impl StepIvrApp {
163163
total_steps: 0,
164164
status: "active".to_string(),
165165
};
166-
tokio::spawn(async move {
166+
crate::utils::spawn(async move {
167167
t.record_session(sess).await;
168168
});
169169
}
@@ -179,7 +179,7 @@ impl StepIvrApp {
179179
if let Some(t) = self.effective_trace() {
180180
let sid = session_id;
181181
let st = status.to_string();
182-
tokio::spawn(async move {
182+
crate::utils::spawn(async move {
183183
t.update_session_end(&sid, chrono::Utc::now(), &st).await;
184184
});
185185
}
@@ -1240,7 +1240,7 @@ mod tests {
12401240
);
12411241
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
12421242
let addr = listener.local_addr().unwrap();
1243-
tokio::spawn(async move {
1243+
crate::utils::spawn(async move {
12441244
axum::serve(listener, app).await.ok();
12451245
});
12461246
format!("http://{}:{}/ivr/step", addr.ip(), addr.port())

src/call/app/ivr_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ action = { type = "transfer", target = "100" }
10621062
.await
10631063
.expect("bind");
10641064

1065-
tokio::spawn(async move {
1065+
crate::utils::spawn(async move {
10661066
axum::serve(listener, router).await.ok();
10671067
});
10681068

@@ -2614,7 +2614,7 @@ action = { type = "transfer", target = "100" }
26142614

26152615
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
26162616
let port = listener.local_addr().unwrap().port();
2617-
tokio::spawn(async move {
2617+
crate::utils::spawn(async move {
26182618
axum::serve(listener, app).await.ok();
26192619
});
26202620

src/call/app/testing.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ impl MockCallStack {
9595
let cancel = CancellationToken::new();
9696
let event_loop = AppEventLoop::new(app, controller, ctx, cancel.child_token(), timer_rx);
9797

98-
let join_handle = tokio::spawn(event_loop.run());
98+
let join_handle = crate::utils::spawn(event_loop.run());
9999

100100
Self {
101101
event_tx,

0 commit comments

Comments
 (0)