diff --git a/lib/amr.c b/lib/amr.c index cf50c567b..af1c5b116 100644 --- a/lib/amr.c +++ b/lib/amr.c @@ -122,7 +122,11 @@ static void amr_parse_format_cb(str *key, str *token, void *data) { } } static bool amr_format_parse(struct rtp_codec_format *f, const str *fmtp) { - codeclib_key_value_parse(fmtp, true, amr_parse_format_cb, f); + // Per RFC 4867 section 8.1, missing fmtp parameters use defaults (bandwidth-efficient mode, no + // CRC, no robust-sorting, no interleaving). Parse whatever is present. Absent fields keep their + // defaults. + if (fmtp && fmtp->len) + codeclib_key_value_parse(fmtp, true, amr_parse_format_cb, f); return true; } static void amr_set_encdec_options(codec_options_t *opts, codec_def_t *def) { diff --git a/lib/codeclib.c b/lib/codeclib.c index 3c1ff2356..e65428a36 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -149,8 +149,6 @@ bool codec_parse_fmtp(codec_def_t *def, struct rtp_codec_format *fmtp, const str return false; if (!def->format_parse) return true; - if (!fmtp_string) - return true; if (!fmtp) { ZERO(fmtp_store); fmtp = &fmtp_store; @@ -160,6 +158,8 @@ bool codec_parse_fmtp(codec_def_t *def, struct rtp_codec_format *fmtp, const str *copy = fmtp->parsed; return true; } + // Call format_parse even without fmtp_string so that codecs can populate defaults and set + // fmtp_parsed = true (e.g. AMR with RFC 4867 defaults). bool ret = def->format_parse(fmtp, fmtp_string); if (ret) { fmtp->fmtp_parsed = true; diff --git a/lib/ilbc.c b/lib/ilbc.c index 7fd13efb6..c1c96af09 100644 --- a/lib/ilbc.c +++ b/lib/ilbc.c @@ -3,6 +3,8 @@ static bool ilbc_format_parse(struct rtp_codec_format *f, const str *fmtp) { + if (!fmtp || !fmtp->len) + return false; switch (__csh_lookup(fmtp)) { case CSH_LOOKUP("mode=20"): f->parsed.ilbc.mode = 20; diff --git a/t/auto-daemon-tests-unknown-fmtp.pl b/t/auto-daemon-tests-unknown-fmtp.pl index 272dd99df..c6fb10a54 100644 --- a/t/auto-daemon-tests-unknown-fmtp.pl +++ b/t/auto-daemon-tests-unknown-fmtp.pl @@ -207,4 +207,196 @@ SDP #done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit; + +# AMR-WB with octet-align=0 in offer, answer without fmtp (defaults match) +new_call; + +offer('AMR-WB octet-align=0 offer, answer missing fmtp - should match', { + 'rtpp-flags' => 'replace-origin address-family=IP4 transport-protocol=RTP/AVP', + 'from-tag' => ft(), + }, < 'replace-origin address-family=IP4 transport-protocol=RTP/AVP', + 'from-tag' => ft(), + 'to-tag' => tt(), + }, < 'replace-origin address-family=IP4 transport-protocol=RTP/AVP', + 'from-tag' => ft(), + }, < 'replace-origin address-family=IP4 transport-protocol=RTP/AVP', + 'from-tag' => ft(), + 'to-tag' => tt(), + }, < 'replace-origin address-family=IP4 transport-protocol=RTP/AVP', + 'from-tag' => ft(), + }, < 'replace-origin address-family=IP4 transport-protocol=RTP/AVP', + 'from-tag' => ft(), + 'to-tag' => tt(), + }, < should match + start(); + sdp_pt(0, PCMU, 8000); + transcode_s("AMR-WB/16000/1///octet-align=0"); + offer(); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 96/AMR-WB/16000/octet-align=0"); + sdp_pt(96, AMR-WB, 16000); // answer without fmtp + answer(); + expect(A, "0/PCMU/8000"); + expect(B, "96/AMR-WB/16000"); + end(); + + // forward AMR-WB, answer without fmtp but offer has octet-align=1 + // these are incompatible + start(); + sdp_pt(0, PCMU, 8000); + transcode(AMR-WB); + offer(); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 96/AMR-WB/16000/octet-align=1;mode-change-capability=2"); + sdp_pt(0, PCMU, 8000); + sdp_pt(96, AMR-WB, 16000); // answer with PCMU + AMR-WB (no fmtp implies octet-align=0) + answer(); + // AMR-WB should be rejected as incompatible (octet-align=0 != octet-align=1), only PCMU remains + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000"); + end(); + + // forward AMR-WB, answer with matching octet-align=1 + start(); + sdp_pt(0, PCMU, 8000); + transcode(AMR-WB); + offer(); + expect(A, "0/PCMU/8000"); + expect(B, "0/PCMU/8000 96/AMR-WB/16000/octet-align=1;mode-change-capability=2"); + sdp_pt_fmt(96, AMR-WB, 16000, "octet-align=1"); + answer(); + expect(A, "0/PCMU/8000"); + expect(B, "96/AMR-WB/16000/octet-align=1"); + end(); } }