mgcp: Set net_end audio params in recvonly mode

Currently, if there is no SDP data in the MGCP message received from
the net, the fields containing audio encoding information are not set
in net_end. So in recvonly mode transcoding would not be set up
correctly.

This patch changes the implementation of the code handling CRCX and
MDCX to use the codec signalled in the MGCP local connection options
(field 'a:') if there isn't any SDP data. This is only halfway
negotiation, because the codec is used blindly and not matched
against the supported ones.

Sponsored-by: On-Waves ehf
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h
index 9f0c0f9..2b44e40 100644
--- a/openbsc/include/openbsc/mgcp_internal.h
+++ b/openbsc/include/openbsc/mgcp_internal.h
@@ -123,6 +123,7 @@
 
 struct mgcp_lco {
 	char *string;
+	char *codec;
 	int pkt_period_min; /* time in ms */
 	int pkt_period_max; /* time in ms */
 };
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index 21b9ff0..f0457d1 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -80,6 +80,8 @@
 #define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000
 #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
 
+#define PTYPE_UNDEFINED (-1)
+
 static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
 
 struct mgcp_parse_data {
@@ -598,7 +600,8 @@
 	talloc_free(rtp->audio_name);
 	rtp->audio_name = NULL;
 
-	rtp->payload_type = payload_type;
+	if (payload_type != PTYPE_UNDEFINED)
+		rtp->payload_type = payload_type;
 
 	if (!audio_name) {
 		switch (payload_type) {
@@ -614,7 +617,7 @@
 	}
 
 	if (sscanf(audio_name, "%63[^/]/%d/%d",
-		   audio_codec, &rate, &channels) < 2)
+		   audio_codec, &rate, &channels) < 1)
 		return -EINVAL;
 
 	rtp->rate = rate;
@@ -630,6 +633,20 @@
 		rtp->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
 	}
 
+	if (payload_type < 0) {
+		payload_type = 96;
+		if (rate == 8000 && channels == 1) {
+			if (!strcmp(audio_codec, "GSM"))
+				payload_type = 3;
+			else if (!strcmp(audio_codec, "PCMA"))
+				payload_type = 8;
+			else if (!strcmp(audio_codec, "G729"))
+				payload_type = 18;
+		}
+
+		rtp->payload_type = payload_type;
+	}
+
 	if (channels != 1)
 		LOGP(DMGCP, LOGL_NOTICE,
 		     "Channels != 1 in SDP: '%s'\n", audio_name);
@@ -801,9 +818,12 @@
 static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
 				 const char *options)
 {
-	char *p_opt;
+	char *p_opt, *a_opt;
+	char codec[9];
 
 	talloc_free(lco->string);
+	talloc_free(lco->codec);
+	lco->codec = NULL;
 	lco->pkt_period_min = lco->pkt_period_max = 0;
 	lco->string = talloc_strdup(ctx, options ? options : "");
 
@@ -811,6 +831,10 @@
 	if (p_opt && sscanf(p_opt, "p:%d-%d",
 			    &lco->pkt_period_min, &lco->pkt_period_max) == 1)
 		lco->pkt_period_max = lco->pkt_period_min;
+
+	a_opt = strstr(lco->string, "a:");
+	if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
+		lco->codec = talloc_strdup(ctx, codec);
 }
 
 void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
@@ -955,6 +979,9 @@
 						tcfg->audio_fmtp_extra);
 	if (have_sdp)
 		parse_sdp_data(&endp->net_end, p);
+	else if (endp->local_options.codec)
+		set_audio_info(p->cfg, &endp->net_end,
+			       PTYPE_UNDEFINED, endp->local_options.codec);
 
 	if (p->cfg->bts_force_ptime) {
 		endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime;
@@ -1012,6 +1039,7 @@
 	struct mgcp_endpoint *endp = p->endp;
 	int error_code = 500;
 	int silent = 0, osmux = 0;
+	int have_sdp = 0;
 	char *line;
 	const char *local_options = NULL;
 
@@ -1058,6 +1086,7 @@
 			break;
 		case '\0':
 			/* SDP file begins */
+			have_sdp = 1;
 			parse_sdp_data(&endp->net_end, p);
 			/* This will exhaust p->save, so the loop will
 			 * terminate next time.
@@ -1088,6 +1117,10 @@
 	set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
 			     local_options);
 
+	if (!have_sdp && endp->local_options.codec)
+		set_audio_info(p->cfg, &endp->net_end,
+			       PTYPE_UNDEFINED, endp->local_options.codec);
+
 	setup_rtp_processing(endp);
 
 	/* policy CB */
@@ -1462,6 +1495,8 @@
 
 	talloc_free(endp->local_options.string);
 	endp->local_options.string = NULL;
+	talloc_free(endp->local_options.codec);
+	endp->local_options.codec = NULL;
 
 	mgcp_rtp_end_reset(&endp->bts_end);
 	mgcp_rtp_end_reset(&endp->net_end);
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index 5388483..19615d9 100644
--- a/openbsc/tests/mgcp/mgcp_test.c
+++ b/openbsc/tests/mgcp/mgcp_test.c
@@ -168,6 +168,12 @@
 		 "a=rtpmap:99 AMR/8000\r\n"	\
 		 "a=ptime:40\r\n"
 
+#define MDCX4_RO "MDCX 18983221 1@mgw MGCP 1.0\r\n" \
+		 "M: recvonly\r"		\
+		 "C: 2\r\n"          \
+		 "I: 1\r\n"                    \
+		 "L: p:20, a:AMR, nt:IN\r\n"
+
 #define SHORT2	"CRCX 1"
 #define SHORT2_RET "510 000000 FAIL\r\n"
 #define SHORT3	"CRCX 1 1@mgw"
@@ -258,6 +264,7 @@
 	{ "MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99, 126 },
 	{ "MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99, 126 },
 	{ "MDCX4_SO", MDCX4_SO, MDCX4_RET("18983220"), 99, 126 },
+	{ "MDCX4_RO", MDCX4_RO, MDCX4_RET("18983221"), PTYPE_IGNORE, 126 },
 	{ "DLCX", DLCX, DLCX_RET, -1, -1 },
 	{ "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97, 126 },
 	{ "EMPTY", EMPTY, EMPTY_RET },
diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok
index 3901cfb..7301a81 100644
--- a/openbsc/tests/mgcp/mgcp_test.ok
+++ b/openbsc/tests/mgcp/mgcp_test.ok
@@ -49,6 +49,11 @@
 Detected packet duration: 40
 Requested packetetization period: 20-20
 Connection mode: 2: SEND
+Testing MDCX4_RO
+Dummy packets: 1
+Packet duration not set
+Requested packetetization period: 20-20
+Connection mode: 1: RECV
 Testing DLCX
 Detected packet duration: 20
 Requested packetization period not set