diff --git a/changelog.md b/changelog.md index 9e30479..b74781d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-04-10 - 1.17.2 - fix(proxy-engine) +use negotiated SDP payload types when wiring SIP legs and enable default nnnoiseless features for telephony denoising + +- Select the negotiated codec payload type from SDP answers instead of always using the first offered codec +- Preserve the device leg's preferred payload type from its own INVITE SDP when attaching it to the mixer +- Enable default nnnoiseless features in codec-lib and proxy-engine dependencies + ## 2026-04-10 - 1.17.1 - fix(proxy-engine,codec-lib,sip-proto,ts) preserve negotiated media details and improve RTP audio handling across call legs diff --git a/nogit/voicemail/default/msg-1775840439400.wav b/nogit/voicemail/default/msg-1775840439400.wav new file mode 100644 index 0000000..3a294b4 Binary files /dev/null and b/nogit/voicemail/default/msg-1775840439400.wav differ diff --git a/nogit/voicemail/default/msg-1775840447441.wav b/nogit/voicemail/default/msg-1775840447441.wav new file mode 100644 index 0000000..425605f Binary files /dev/null and b/nogit/voicemail/default/msg-1775840447441.wav differ diff --git a/nogit/voicemail/default/msg-1775840454835.wav b/nogit/voicemail/default/msg-1775840454835.wav new file mode 100644 index 0000000..f4abe9d Binary files /dev/null and b/nogit/voicemail/default/msg-1775840454835.wav differ diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 928d7b6..a7e2ca5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -237,6 +237,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "audiopus" version = "0.3.0-rc.0" @@ -487,6 +498,31 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "once_cell", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cmake" version = "0.1.58" @@ -700,6 +736,125 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06d2e3287df1c007e74221c49ca10a95d557349e54b3a75dc2fb14712c751f04" +[[package]] +name = "dasp" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7381b67da416b639690ac77c73b86a7b5e64a29e31d1f75fb3b1102301ef355a" +dependencies = [ + "dasp_envelope", + "dasp_frame", + "dasp_interpolate", + "dasp_peak", + "dasp_ring_buffer", + "dasp_rms", + "dasp_sample", + "dasp_signal", + "dasp_slice", + "dasp_window", +] + +[[package]] +name = "dasp_envelope" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec617ce7016f101a87fe85ed44180839744265fae73bb4aa43e7ece1b7668b6" +dependencies = [ + "dasp_frame", + "dasp_peak", + "dasp_ring_buffer", + "dasp_rms", + "dasp_sample", +] + +[[package]] +name = "dasp_frame" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a3937f5fe2135702897535c8d4a5553f8b116f76c1529088797f2eee7c5cd6" +dependencies = [ + "dasp_sample", +] + +[[package]] +name = "dasp_interpolate" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc975a6563bb7ca7ec0a6c784ead49983a21c24835b0bc96eea11ee407c7486" +dependencies = [ + "dasp_frame", + "dasp_ring_buffer", + "dasp_sample", +] + +[[package]] +name = "dasp_peak" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cf88559d79c21f3d8523d91250c397f9a15b5fc72fbb3f87fdb0a37b79915bf" +dependencies = [ + "dasp_frame", + "dasp_sample", +] + +[[package]] +name = "dasp_ring_buffer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07d79e19b89618a543c4adec9c5a347fe378a19041699b3278e616e387511ea1" + +[[package]] +name = "dasp_rms" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c5dcb30b7e5014486e2822537ea2beae50b19722ffe2ed7549ab03774575aa" +dependencies = [ + "dasp_frame", + "dasp_ring_buffer", + "dasp_sample", +] + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "dasp_signal" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa1ab7d01689c6ed4eae3d38fe1cea08cba761573fbd2d592528d55b421077e7" +dependencies = [ + "dasp_envelope", + "dasp_frame", + "dasp_interpolate", + "dasp_peak", + "dasp_ring_buffer", + "dasp_rms", + "dasp_sample", + "dasp_window", +] + +[[package]] +name = "dasp_slice" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e1c7335d58e7baedafa516cb361360ff38d6f4d3f9d9d5ee2a2fc8e27178fa1" +dependencies = [ + "dasp_frame", + "dasp_sample", +] + +[[package]] +name = "dasp_window" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ded7b88821d2ce4e8b842c9f1c86ac911891ab89443cc1de750cae764c5076" +dependencies = [ + "dasp_sample", +] + [[package]] name = "data-encoding" version = "2.10.0" @@ -1214,6 +1369,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1246,6 +1407,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -1446,6 +1616,16 @@ dependencies = [ "zstd", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -1739,7 +1919,13 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "805d5964d1e7a0006a7fdced7dae75084d66d18b35f1dfe81bd76929b1f8da0c" dependencies = [ + "anyhow", + "clap", + "dasp", + "dasp_interpolate", + "dasp_ring_buffer", "easyfft", + "hound", "once_cell", ] @@ -1905,6 +2091,12 @@ dependencies = [ "ureq", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "p256" version = "0.11.1" @@ -2883,6 +3075,21 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" + [[package]] name = "thiserror" version = "1.0.69" @@ -3244,7 +3451,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -3257,7 +3464,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.0", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.14.0", "semver", ] @@ -3515,6 +3722,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3564,7 +3780,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.14.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -3595,7 +3811,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.0", - "indexmap", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -3614,7 +3830,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.14.0", "log", "semver", "serde", diff --git a/rust/crates/codec-lib/Cargo.toml b/rust/crates/codec-lib/Cargo.toml index 10ba784..112c3dc 100644 --- a/rust/crates/codec-lib/Cargo.toml +++ b/rust/crates/codec-lib/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" audiopus = "0.3.0-rc.0" ezk-g722 = "0.1" rubato = "0.14" -nnnoiseless = { version = "0.5", default-features = false } +nnnoiseless = "0.5" diff --git a/rust/crates/proxy-engine/Cargo.toml b/rust/crates/proxy-engine/Cargo.toml index c7970f8..8c0c59e 100644 --- a/rust/crates/proxy-engine/Cargo.toml +++ b/rust/crates/proxy-engine/Cargo.toml @@ -10,7 +10,7 @@ path = "src/main.rs" [dependencies] codec-lib = { path = "../codec-lib" } sip-proto = { path = "../sip-proto" } -nnnoiseless = { version = "0.5", default-features = false } +nnnoiseless = "0.5" tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/rust/crates/proxy-engine/src/call_manager.rs b/rust/crates/proxy-engine/src/call_manager.rs index 39827f4..402c94d 100644 --- a/rust/crates/proxy-engine/src/call_manager.rs +++ b/rust/crates/proxy-engine/src/call_manager.rs @@ -196,7 +196,17 @@ impl CallManager { }; // Mutable borrow on call/leg is now released. - let sip_pt = codecs.first().copied().unwrap_or(9); + let mut sip_pt = codecs.first().copied().unwrap_or(9); + + // If the message has SDP (e.g., 200 OK answer), use the negotiated codec + // instead of the offered one. + if msg.has_sdp_body() { + if let Some(ep) = parse_sdp_endpoint(&msg.body) { + if let Some(pt) = ep.codec_pt { + sip_pt = pt; + } + } + } match action { SipLegAction::None => {} @@ -301,12 +311,20 @@ impl CallManager { ); // Wire device leg to mixer. + // Use the device's preferred codec from its INVITE SDP, + // not the provider's negotiated codec. + let dev_pt = device_invite + .has_sdp_body() + .then(|| parse_sdp_endpoint(&device_invite.body)) + .flatten() + .and_then(|ep| ep.codec_pt) + .unwrap_or(sip_pt); if let Some(dev_remote_addr) = dev_remote { let dev_channels = create_leg_channels(); spawn_sip_inbound(dev_rtp_socket.clone(), dev_channels.inbound_tx); spawn_sip_outbound(dev_rtp_socket, dev_remote_addr, dev_channels.outbound_rx); if let Some(call) = self.calls.get(call_id) { - call.add_leg_to_mixer(&dev_leg_id, sip_pt, dev_channels.inbound_rx, dev_channels.outbound_tx) + call.add_leg_to_mixer(&dev_leg_id, dev_pt, dev_channels.inbound_rx, dev_channels.outbound_tx) .await; } } diff --git a/rust/crates/proxy-engine/src/mixer.rs b/rust/crates/proxy-engine/src/mixer.rs index ccbbc8c..4d57dbb 100644 --- a/rust/crates/proxy-engine/src/mixer.rs +++ b/rust/crates/proxy-engine/src/mixer.rs @@ -368,8 +368,8 @@ async fn mixer_loop( .unwrap_or_else(|_| vec![0.0f32; MIX_FRAME_SIZE]) }; // Per-leg inbound denoising at 48kHz. - // Skip for Opus/WebRTC legs — browsers already apply - // their own noise suppression via getUserMedia. + // Only for SIP telephony legs — WebRTC browsers + // already apply noise suppression via getUserMedia. let processed = if slot.codec_pt != codec_lib::PT_OPUS { TranscodeState::denoise_f32(&mut slot.denoiser, &pcm_48k) } else { diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 68e53fc..75ecc8b 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: 'siprouter', - version: '1.17.1', + version: '1.17.2', description: 'undefined' } diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 68e53fc..75ecc8b 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: 'siprouter', - version: '1.17.1', + version: '1.17.2', description: 'undefined' }