engine/sdp: CODECS override for the codecs we offer and accept

KPN's interconnect gateway transcodes every caller up: a G.711-only
fixed-line INVITE reached the phone offering PCMA, PCMU, AMR and AMR-WB
(bench call 2026-09-08), so the AMR-WB-only build rang and the narrowband
path — the one the Telia field report hit with 488 — cannot be reached on
the air through KPN by any caller.

CODECS=<list> (comma-separated over AMR-WB, AMR/AMR-NB, PCMA, PCMU) makes
the list both the codecs offered on an outgoing call and the acceptance
preference for an inbound offer, in that order: CODECS=PCMA takes G.711
A-law out of the mixed offer above, CODECS=PCMA,PCMU sends a G.711-only
offer toward the network. Unset, nothing changes — the default offer
bytes and the AMR-WB > AMR > PCMA > PCMU preference stay pinned. The
daemon logs an active override at startup.
This commit is contained in:
Jorijn van der Graaf 2026-09-08 17:53:26 +02:00
commit 37ec8660b7
6 changed files with 270 additions and 67 deletions

View file

@ -772,6 +772,62 @@ int main() {
Check(m.Terminated(), "abandoned machine is terminated");
}
// ---- CODECS override, terminating side: the transcoding gateway's
// mixed offer (KPN 2026-09-08) answered with G.711 A-law because the
// list says so — the on-air narrowband bench path
{
imsd::util::Rng rng(51);
std::string sdp =
"v=0\r\no=LucentPCSF 1 1 IN IP6 2001:db8::309\r\ns=-\r\n"
"c=IN IP6 2001:db8::309\r\nt=0 0\r\n"
"m=audio 41166 RTP/AVP 8 0 100 96 101 13 106\r\n"
"a=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:100 AMR/8000\r\na=fmtp:100 mode-change-period=2\r\n"
"a=rtpmap:96 AMR-WB/16000\r\na=fmtp:96 mode-change-period=2\r\n"
"a=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-15\r\n"
"a=rtpmap:106 telephone-event/16000\r\na=fmtp:106 0-15\r\n"
"a=sendrecv\r\na=ptime:20\r\na=maxptime:60\r\n";
CallMachine m(c, rng, "ims-call-20", IncomingInvite{MtInviteSdp("mt-kpn-pcma", sdp)}, 50004, {"PCMA"});
auto ai = m.OnInvite();
Check(Responded(ai, "SIP/2.0 180 Ringing") != nullptr, "CODECS=PCMA: mixed offer rings");
auto aa = m.OnAccept();
const Action* ok = Responded(aa, "SIP/2.0 200 OK");
Check(ok && ok->text.contains("m=audio 50004 RTP/AVP 8 101\r\n"), "CODECS=PCMA: answer is PCMA + narrowband DTMF at the offer's pts");
Check(ok && ok->text.contains("a=rtpmap:8 PCMA/8000/1\r\n") && !ok->text.contains("AMR"), "CODECS=PCMA: no AMR in the answer");
const Action* med = Find(aa, Action::Type::StartMedia);
Check(med && med->media.codec == "PCMA" && med->media.payloadType == 8 && !med->media.octetAlign, "CODECS=PCMA: media leg starts G.711 A-law");
// same offer, CODECS=AMR: narrowband AMR, bandwidth-efficient as offered
imsd::util::Rng rng2(52);
CallMachine m2(c, rng2, "ims-call-21", IncomingInvite{MtInviteSdp("mt-kpn-amr", sdp)}, 50004, {"AMR"});
m2.OnInvite();
auto aa2 = m2.OnAccept();
const Action* ok2 = Responded(aa2, "SIP/2.0 200 OK");
Check(ok2 && ok2->text.contains("m=audio 50004 RTP/AVP 100 101\r\n") && !ok2->text.contains("octet-align"), "CODECS=AMR: answer is AMR-NB BE + narrowband DTMF");
const Action* med2 = Find(aa2, Action::Type::StartMedia);
Check(med2 && med2->media.codec == "AMR" && med2->media.payloadType == 100 && !med2->media.octetAlign, "CODECS=AMR: media leg starts AMR-NB BE");
// same offer, no override: unchanged — AMR-WB
imsd::util::Rng rng3(53);
CallMachine m3(c, rng3, "ims-call-22", IncomingInvite{MtInviteSdp("mt-kpn-default", sdp)}, 50004);
m3.OnInvite();
auto aa3 = m3.OnAccept();
const Action* med3 = Find(aa3, Action::Type::StartMedia);
Check(med3 && med3->media.codec == "AMR-WB" && med3->media.payloadType == 96, "no override: the same offer still lands on AMR-WB");
}
// ---- CODECS override, originating side: the INVITE offers only what the
// list names (G.711 = a landline-shaped offer toward the network)
{
imsd::util::Rng rng(54);
CallMachine m(c, rng, "ims-call-23", "0783690354", 50004, false, false, {"PCMA", "PCMU"});
auto start = m.Start();
const Action* inv = Sent(start, "INVITE ");
Check(inv && inv->text.contains("m=audio 50004 RTP/AVP 8 0 100\r\n"), "CODECS=PCMA,PCMU: INVITE offers G.711 only");
Check(inv && !inv->text.contains("AMR"), "CODECS=PCMA,PCMU: no AMR in the offer");
Check(inv && inv->text.contains("a=rtpmap:100 telephone-event/8000\r\n") && !inv->text.contains("telephone-event/16000"), "CODECS=PCMA,PCMU: narrowband DTMF only");
}
if (Failures == 0) std::println("Engine: all tests passed");
return Failures;
}

View file

@ -294,6 +294,93 @@ int main() {
Check(sdp.contains("a=rtpmap:0 PCMU/8000/1\r\n") && !sdp.contains("fmtp:0"), "PCMU answer has no fmtp");
}
// ---- CODECS override: the offer of a real interconnect gateway that
// transcodes up (KPN, 2026-09-08: a G.711-only caller arrives with
// AMR-WB BE added). Default picks AMR-WB; a preference list picks
// what it names, in its order, and nothing outside it.
{
std::string kpn =
"c=IN IP6 2001:db8::309\r\n"
"m=audio 41166 RTP/AVP 8 0 100 96 101 13 106\r\n"
"a=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\n"
"a=rtpmap:100 AMR/8000\r\na=fmtp:100 mode-change-period=2\r\n"
"a=rtpmap:96 AMR-WB/16000\r\na=fmtp:96 mode-change-period=2\r\n"
"a=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-15\r\n"
"a=rtpmap:106 telephone-event/16000\r\na=fmtp:106 0-15\r\n"
"a=sendrecv\r\na=ptime:20\r\na=maxptime:60\r\n";
Answer d = ParseAnswer(kpn);
Check(d.payloadType == 96 && d.codec == "AMR-WB" && !d.octetAlign, "transcoding gateway: default takes AMR-WB BE");
std::vector<std::string> pcma{"PCMA"};
Answer a = ParseAnswer(kpn, pcma);
Check(a.payloadType == 8 && a.codec == "PCMA" && !a.octetAlign, "CODECS=PCMA takes G.711 A-law out of a mixed offer");
std::vector<std::string> ulawFirst{"PCMU", "PCMA"};
Answer u = ParseAnswer(kpn, ulawFirst);
Check(u.payloadType == 0 && u.codec == "PCMU", "CODECS order wins over the offer's order");
std::vector<std::string> nb{"AMR"};
Answer n = ParseAnswer(kpn, nb);
Check(n.payloadType == 100 && n.codec == "AMR" && !n.octetAlign, "CODECS=AMR takes narrowband AMR, BE as offered");
std::vector<std::string> wb{"AMR-WB"};
Answer w = ParseAnswer(kpn, wb);
Check(w.payloadType == 96 && w.codec == "AMR-WB", "CODECS=AMR-WB matches the default here");
Answer g = ParseAnswer("c=IN IP4 192.0.2.1\r\n" "m=audio 5000 RTP/AVP 8 0 101\r\n" "a=rtpmap:101 telephone-event/8000\r\n", wb);
Check(g.codec.empty() && g.payloadType == 8, "CODECS=AMR-WB refuses a G.711-only offer (codec empty, first pt)");
Check(g.port == 5000 && g.ip == "192.0.2.1", "address/port still parsed when no codec is acceptable");
}
// ---- ParseCodecList: case, spaces, AMR-NB alias, unknowns, repeats
{
auto l = ParseCodecList("pcma, amr-nb ,AMR-WB,g729,PCMA,,PCMU");
Check(l.size() == 4 && l[0] == "PCMA" && l[1] == "AMR" && l[2] == "AMR-WB" && l[3] == "PCMU", "codec list normalised: PCMA,AMR,AMR-WB,PCMU");
Check(ParseCodecList("").empty(), "empty spec = no override");
Check(ParseCodecList("opus, g722").empty(), "only unknown names = no override");
}
// ---- BuildOffer with an explicit codec list: G.711 only (landline
// shape, for MO tests against a transcoding network)
{
std::string sdp = BuildOffer({
.local = "192.0.2.1",
.rtpPort = 22222,
.sessionId = 1234567,
.codecs = {"PCMA", "PCMU"},
});
std::string expected =
"v=0\r\n"
"o=- 1234567 1234567 IN IP4 192.0.2.1\r\n"
"s=-\r\n"
"c=IN IP4 192.0.2.1\r\n"
"t=0 0\r\n"
"m=audio 22222 RTP/AVP 8 0 100\r\n"
"b=AS:80\r\n"
"b=RS:512\r\n"
"b=RR:1536\r\n"
"a=rtpmap:8 PCMA/8000/1\r\n"
"a=rtpmap:0 PCMU/8000/1\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";
Check(sdp == expected, "G.711-only offer byte-exact");
}
// ---- BuildOffer, AMR narrowband only: pt 99, narrowband DTMF only, AS 30
{
std::string sdp = BuildOffer({.local = "2001:db8::1", .rtpPort = 50004, .sessionId = 7, .codecs = {"AMR"}});
Check(sdp.contains("m=audio 50004 RTP/AVP 99 100\r\n"), "AMR-only offer m-line");
Check(sdp.contains("b=AS:30\r\n"), "AMR-only offer bandwidth");
Check(sdp.contains("a=rtpmap:99 AMR/8000/1\r\na=fmtp:99 octet-align=1;mode-change-capability=2;max-red=0\r\n"), "AMR-only offer rtpmap+fmtp");
Check(!sdp.contains("16000"), "no wideband telephone-event without AMR-WB");
}
// ---- BuildOffer, explicit default order == the pinned default bytes
{
Offer o{.local = "2001:db8::1", .rtpPort = 50004, .sessionId = 7};
Offer e = o;
e.codecs = {"AMR-WB", "AMR"};
Check(BuildOffer(o) == BuildOffer(e), "explicit AMR-WB,AMR list is byte-identical to the default offer");
}
if (Failures == 0) std::println("Sdp: all tests passed");
return Failures;
}