From d5aaf57e49cc2fc41c6162fec2616d6a103c6f7d Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Wed, 2 Sep 2026 03:21:06 +0200 Subject: [PATCH] sdp: offer AMR narrowband after AMR-WB Our offer listed AMR-WB alone. A call we place TO a landline then depends on the far side transcoding up to wideband; a gateway that will not is entitled to refuse. AMR narrowband is the 3GPP-mandatory codec every IMS core and PSTN gateway can answer, so it goes second, with telephone-event at the 8 kHz clock beside the existing 16 kHz one. AMR-WB stays first, so a mobile-to-mobile call negotiates exactly what it did before; if the network does answer AMR, the engine already starts the media leg on the answered codec. Byte pins in tests/Sdp and tests/Messages re-pinned (INVITE body 323 -> 466 bytes). NOTE FOR RELEASE: this changes the MO INVITE on the path the arranged 112 calls used. One bench MO call on KPN before it ships. --- interfaces/Imsd-Sdp.cppm | 18 +++++++++++++----- tests/Messages/main.cpp | 14 +++++++++++--- tests/Sdp/main.cpp | 6 +++++- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/interfaces/Imsd-Sdp.cppm b/interfaces/Imsd-Sdp.cppm index 5a9eaaa..471e294 100644 --- a/interfaces/Imsd-Sdp.cppm +++ b/interfaces/Imsd-Sdp.cppm @@ -5,9 +5,9 @@ /* Imsd:Sdp — SDP offer/answer building + parsing for the voice media plane. -The offer is a fixed AMR-WB (octet-aligned) + telephone-event shape with an -optional GSMA IR.92 QoS-precondition block; it is what commercial IMS cores -expect from a VoLTE UE, and its exact bytes are pinned by tests/Sdp. The +The offer is a fixed AMR-WB (octet-aligned) + AMR + telephone-event shape +with an optional GSMA IR.92 QoS-precondition block; it is what commercial IMS +cores expect from a VoLTE UE, and its exact bytes are pinned by tests/Sdp. The parser extracts only what the media plane needs: remote address/port, the negotiated payload type, the codec and octet-align mode — it reads answers to our offers and inbound offers alike (same fields either way). The codecs @@ -57,7 +57,11 @@ export namespace imsd::sdp { std::string currRemote = "none"; // a=curr:qos remote }; - // AMR-WB (octet-aligned) offer. Byte layout is pinned by tests/Sdp. + // Our offer: AMR-WB (octet-aligned) first, AMR narrowband second — the + // 3GPP-mandatory codec every IMS core and PSTN gateway can answer, so a + // call we place TO a landline no longer depends on the far side + // transcoding up to wideband — each with telephone-event at its own + // clock. Byte layout is pinned by tests/Sdp. std::string BuildOffer(const Offer& o) { std::string ipver = Is6(o.local) ? "IP6" : "IP4"; std::string s; @@ -66,14 +70,18 @@ export namespace imsd::sdp { s += "s=-\r\n"; s += std::format("c=IN {} {}\r\n", ipver, o.local); s += "t=0 0\r\n"; - s += std::format("m=audio {} RTP/AVP 97 98\r\n", o.rtpPort); + s += std::format("m=audio {} RTP/AVP 97 99 98 100\r\n", o.rtpPort); s += "b=AS:41\r\n"; s += "b=RS:512\r\n"; s += "b=RR:1536\r\n"; s += "a=rtpmap:97 AMR-WB/16000/1\r\n"; s += "a=fmtp:97 octet-align=1;mode-change-capability=2;max-red=0\r\n"; + s += "a=rtpmap:99 AMR/8000/1\r\n"; + s += "a=fmtp:99 octet-align=1;mode-change-capability=2;max-red=0\r\n"; s += "a=rtpmap:98 telephone-event/16000\r\n"; s += "a=fmtp:98 0-15\r\n"; + s += "a=rtpmap:100 telephone-event/8000\r\n"; + s += "a=fmtp:100 0-15\r\n"; s += "a=ptime:20\r\n"; s += "a=maxptime:240\r\n"; if (o.precond) { diff --git a/tests/Messages/main.cpp b/tests/Messages/main.cpp index 7d308f1..3ee9ba2 100644 --- a/tests/Messages/main.cpp +++ b/tests/Messages/main.cpp @@ -96,14 +96,18 @@ namespace { "s=-\r\n" "c=IN IP6 2001:db8::db43\r\n" "t=0 0\r\n" - "m=audio 50004 RTP/AVP 97 98\r\n" + "m=audio 50004 RTP/AVP 97 99 98 100\r\n" "b=AS:41\r\n" "b=RS:512\r\n" "b=RR:1536\r\n" "a=rtpmap:97 AMR-WB/16000/1\r\n" "a=fmtp:97 octet-align=1;mode-change-capability=2;max-red=0\r\n" + "a=rtpmap:99 AMR/8000/1\r\n" + "a=fmtp:99 octet-align=1;mode-change-capability=2;max-red=0\r\n" "a=rtpmap:98 telephone-event/16000\r\n" "a=fmtp:98 0-15\r\n" + "a=rtpmap:100 telephone-event/8000\r\n" + "a=fmtp:100 0-15\r\n" "a=ptime:20\r\n" "a=maxptime:240\r\n" "a=sendrecv\r\n"; @@ -129,21 +133,25 @@ namespace { "Session-Expires: 1800;refresher=uac\r\n" "Min-SE: 90\r\n" "Content-Type: application/sdp\r\n" - "Content-Length: 323\r\n" + "Content-Length: 466\r\n" "\r\n" "v=0\r\n" "o=- 7777777 7777777 IN IP6 2001:db8::db43\r\n" "s=-\r\n" "c=IN IP6 2001:db8::db43\r\n" "t=0 0\r\n" - "m=audio 50004 RTP/AVP 97 98\r\n" + "m=audio 50004 RTP/AVP 97 99 98 100\r\n" "b=AS:41\r\n" "b=RS:512\r\n" "b=RR:1536\r\n" "a=rtpmap:97 AMR-WB/16000/1\r\n" "a=fmtp:97 octet-align=1;mode-change-capability=2;max-red=0\r\n" + "a=rtpmap:99 AMR/8000/1\r\n" + "a=fmtp:99 octet-align=1;mode-change-capability=2;max-red=0\r\n" "a=rtpmap:98 telephone-event/16000\r\n" "a=fmtp:98 0-15\r\n" + "a=rtpmap:100 telephone-event/8000\r\n" + "a=fmtp:100 0-15\r\n" "a=ptime:20\r\n" "a=maxptime:240\r\n" "a=sendrecv\r\n"; diff --git a/tests/Sdp/main.cpp b/tests/Sdp/main.cpp index f119884..5a70cc9 100644 --- a/tests/Sdp/main.cpp +++ b/tests/Sdp/main.cpp @@ -37,14 +37,18 @@ int main() { "s=-\r\n" "c=IN IP6 2001:db8:29e9:a05f::1\r\n" "t=0 0\r\n" - "m=audio 22222 RTP/AVP 97 98\r\n" + "m=audio 22222 RTP/AVP 97 99 98 100\r\n" "b=AS:41\r\n" "b=RS:512\r\n" "b=RR:1536\r\n" "a=rtpmap:97 AMR-WB/16000/1\r\n" "a=fmtp:97 octet-align=1;mode-change-capability=2;max-red=0\r\n" + "a=rtpmap:99 AMR/8000/1\r\n" + "a=fmtp:99 octet-align=1;mode-change-capability=2;max-red=0\r\n" "a=rtpmap:98 telephone-event/16000\r\n" "a=fmtp:98 0-15\r\n" + "a=rtpmap:100 telephone-event/8000\r\n" + "a=fmtp:100 0-15\r\n" "a=ptime:20\r\n" "a=maxptime:240\r\n" "a=curr:qos local sendrecv\r\n"