mgcp: Allow to enforce that the codecs need to match

We have a lot of legacy that I am afraid to break. We have
everything in place to make a good codec selection (e.g. if
we can avoid transcoding, pick the one with best quality or
the lowest speed). Right now I have a specific case where
from all options I want to pick GSM. Guard the codec compat
check behind the disallow transcoding option to make sure
to not break legacy application.
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h
index 763d83b..485a124 100644
--- a/openbsc/include/openbsc/mgcp_internal.h
+++ b/openbsc/include/openbsc/mgcp_internal.h
@@ -317,6 +317,6 @@
 #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
 
 #define PTYPE_UNDEFINED (-1)
-int mgcp_parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p);
+int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p);
 int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
 			int payload_type, const char *audio_name);
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index 5f3eb3a..40ea791 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -828,7 +828,7 @@
 	endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints,
 						tcfg->audio_fmtp_extra);
 	if (have_sdp)
-		mgcp_parse_sdp_data(&endp->net_end, p);
+		mgcp_parse_sdp_data(endp, &endp->net_end, p);
 	else if (endp->local_options.codec)
 		mgcp_set_audio_info(p->cfg, &endp->net_end.codec,
 			       PTYPE_UNDEFINED, endp->local_options.codec);
@@ -931,7 +931,7 @@
 		case '\0':
 			/* SDP file begins */
 			have_sdp = 1;
-			mgcp_parse_sdp_data(&endp->net_end, p);
+			mgcp_parse_sdp_data(endp, &endp->net_end, p);
 			/* This will exhaust p->save, so the loop will
 			 * terminate next time.
 			 */
diff --git a/openbsc/src/libmgcp/mgcp_sdp.c b/openbsc/src/libmgcp/mgcp_sdp.c
index dc87089..33837b9 100644
--- a/openbsc/src/libmgcp/mgcp_sdp.c
+++ b/openbsc/src/libmgcp/mgcp_sdp.c
@@ -154,8 +154,23 @@
 	LOGP(DMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name);
 }
 
+int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec)
+{
+	char *bts_codec;
+	char audio_codec[64];
 
-int mgcp_parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
+	/*
+	 * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go
+	 * by name first.
+	 */
+	bts_codec = endp->tcfg->audio_name;
+	if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1)
+		return 0;
+
+	return strcasecmp(audio_codec, codec->codec_name) == 0;
+}
+
+int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
 {
 	struct sdp_rtp_map codecs[10];
 	int codecs_used = 0;
@@ -243,6 +258,14 @@
 	for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
 		struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
 					&rtp->codec : &rtp->alt_codec;
+
+		if (endp->tcfg->no_audio_transcoding &&
+			!is_codec_compatible(endp, &codecs[i])) {
+			LOGP(DMGCP, LOGL_NOTICE, "Skipping codec %s\n",
+				codecs[i].codec_name);
+			continue;
+		}
+
 		mgcp_set_audio_info(p->cfg, codec,
 					codecs[i].payload_type,
 					codecs[i].map_line);
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index 0f0e06c..d501859 100644
--- a/openbsc/tests/mgcp/mgcp_test.c
+++ b/openbsc/tests/mgcp/mgcp_test.c
@@ -340,6 +340,31 @@
 		 "a=rtpmap:101 FOO/8000\r\n"	\
 		 "a=ptime:40\r\n"
 
+#define CRCX_MULT_GSM_EXACT \
+		"CRCX 259260421 5@mgw MGCP 1.0\r\n"	\
+		"C: 1355c6041e\r\n"			\
+		"I: 3\r\n"				\
+		"L: p:20, a:GSM, nt:IN\r\n"		\
+		"M: recvonly\r\n"			\
+		"\r\n"					\
+		"v=0\r\n"				\
+		"o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
+		"s=-\r\nc=IN IP4 192.168.181.247\r\n"	\
+		"t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
+		"a=rtpmap:0 PCMU/8000\r\n"		\
+		"a=rtpmap:8 PCMA/8000\r\n"		\
+		"a=rtpmap:3 gsm/8000\r\n"		\
+		"a=rtpmap:18 G729/8000\r\n"		\
+		"a=fmtp:18 annexb=no\r\n"		\
+		"a=rtpmap:4 G723/8000\r\n"		\
+		"a=rtpmap:96 iLBC/8000\r\n"		\
+		"a=fmtp:96 mode=20\r\n"			\
+		"a=rtpmap:97 iLBC/8000\r\n"		\
+		"a=fmtp:97 mode=30\r\n"			\
+		"a=rtpmap:101 telephone-event/8000\r\n"	\
+		"a=fmtp:101 0-15\r\n"			\
+		"a=recvonly\r\n"
+
 struct mgcp_test {
 	const char *name;
 	const char *req;
@@ -1011,6 +1036,40 @@
 	OSMO_ASSERT(endp->net_end.codec.payload_type == 18);
 	OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1);
 
+	/* Allocate 5@mgw at select GSM.. */
+	last_endpoint = -1;
+	inp = create_msg(CRCX_MULT_GSM_EXACT);
+	talloc_free(cfg->trunk.audio_name);
+	cfg->trunk.audio_name = "GSM/8000";
+	cfg->trunk.no_audio_transcoding = 1;
+	resp = mgcp_handle_message(cfg, inp);
+	msgb_free(inp);
+	msgb_free(resp);
+
+	OSMO_ASSERT(last_endpoint == 5);
+	endp = &cfg->trunk.endpoints[last_endpoint];
+	OSMO_ASSERT(endp->net_end.codec.payload_type == 3);
+	OSMO_ASSERT(endp->net_end.alt_codec.payload_type == -1);
+
+	/* Check what happens without that flag */
+
+	/* Free the previous endpoint and the data ... */
+	mgcp_release_endp(endp);
+	talloc_free(endp->last_response);
+	talloc_free(endp->last_trans);
+	endp->last_response = endp->last_trans = NULL;
+
+	last_endpoint = -1;
+	inp = create_msg(CRCX_MULT_GSM_EXACT);
+	cfg->trunk.no_audio_transcoding = 0;
+	resp = mgcp_handle_message(cfg, inp);
+	msgb_free(inp);
+	msgb_free(resp);
+
+	OSMO_ASSERT(last_endpoint == 5);
+	endp = &cfg->trunk.endpoints[last_endpoint];
+	OSMO_ASSERT(endp->net_end.codec.payload_type == 0);
+	OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8);
 
 	talloc_free(cfg);
 }