Change in osmo-mgw[master]: AMR: Add function to convert between bw-effient and octet aligned mode

Harald Welte gerrit-no-reply at lists.osmocom.org
Tue Mar 19 13:35:21 UTC 2019


Harald Welte has submitted this change and it was merged. ( https://gerrit.osmocom.org/13159 )

Change subject: AMR: Add function to convert between bw-effient and octet aligned mode
......................................................................

AMR: Add function to convert between bw-effient and octet aligned mode

RFC3267 specifies two framing modes for AMR packets. An octet aligned
mode is specified where all fields of the AMR packets are aligned to
octet boundaries. The second framing mode is the bandwith efficient mode
where the fields are directly packed one after another.

- add paring/generation functions for related SDP fmtp parameters
- add conversion function to convert AMR payload

Depends: libosmo-netif I5b5a0fa644d8dbb1f04f9d7e35312683c7b3d196
Change-Id: I622c01874b25f5049d4f59eb8157e0ea3cbe16ba
Related: OS#3807
---
M TODO-RELEASE
M include/osmocom/mgcp/mgcp_codec.h
M include/osmocom/mgcp/mgcp_common.h
M include/osmocom/mgcp/mgcp_internal.h
M include/osmocom/mgcp_client/mgcp_client.h
M include/osmocom/mgcp_client/mgcp_client_fsm.h
M src/libosmo-mgcp-client/mgcp_client.c
M src/libosmo-mgcp-client/mgcp_client_fsm.c
M src/libosmo-mgcp/mgcp_codec.c
M src/libosmo-mgcp/mgcp_network.c
M src/libosmo-mgcp/mgcp_protocol.c
M src/libosmo-mgcp/mgcp_sdp.c
M tests/mgcp/mgcp_test.c
M tests/mgcp/mgcp_test.ok
14 files changed, 378 insertions(+), 21 deletions(-)

Approvals:
  Jenkins Builder: Verified
  Harald Welte: Looks good to me, approved



diff --git a/TODO-RELEASE b/TODO-RELEASE
index c5a3b36..67a1b21 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -24,3 +24,4 @@
 # If any interfaces have been removed or changed since the last public release, a=0.
 #
 #library		what		description / commit summary line
+libosmo-mgcp-client	add struct members	    AMR SDP/fmtp parameter generation
\ No newline at end of file
diff --git a/include/osmocom/mgcp/mgcp_codec.h b/include/osmocom/mgcp/mgcp_codec.h
index 334913b..2bbb86e 100644
--- a/include/osmocom/mgcp/mgcp_codec.h
+++ b/include/osmocom/mgcp/mgcp_codec.h
@@ -2,6 +2,6 @@
 
 void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
 void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
-int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
+int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, struct mgcp_codec_param *param);
 int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
 int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type);
diff --git a/include/osmocom/mgcp/mgcp_common.h b/include/osmocom/mgcp/mgcp_common.h
index a658f1c..75d5a37 100644
--- a/include/osmocom/mgcp/mgcp_common.h
+++ b/include/osmocom/mgcp/mgcp_common.h
@@ -57,6 +57,12 @@
 	MGCP_X_OSMO_IGN_CALLID = 1,
 };
 
+/* Codec parameters (communicated via SDP/fmtp) */
+struct mgcp_codec_param {
+	bool amr_octet_aligned_present;
+	bool amr_octet_aligned;
+};
+
 /* Ensure that the msg->l2h is NUL terminated. */
 static inline int mgcp_msg_terminate_nul(struct msgb *msg)
 {
diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h
index 35b535a..ec94584 100644
--- a/include/osmocom/mgcp/mgcp_internal.h
+++ b/include/osmocom/mgcp/mgcp_internal.h
@@ -95,6 +95,9 @@
 	int payload_type;
 	char *audio_name;
 	char *subtype_name;
+
+	bool param_present;
+	struct mgcp_codec_param param;
 };
 
 /* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
diff --git a/include/osmocom/mgcp_client/mgcp_client.h b/include/osmocom/mgcp_client/mgcp_client.h
index 5650fc2..ac3e2b1 100644
--- a/include/osmocom/mgcp_client/mgcp_client.h
+++ b/include/osmocom/mgcp_client/mgcp_client.h
@@ -78,7 +78,7 @@
 	enum mgcp_codecs codecs[MGCP_MAX_CODECS];
 	unsigned int codecs_len;
 	struct ptmap ptmap[MGCP_MAX_CODECS];
-	unsigned int ptmap_len;	
+	unsigned int ptmap_len;
 };
 
 enum mgcp_verb {
@@ -113,6 +113,8 @@
 	struct ptmap ptmap[MGCP_MAX_CODECS];
 	unsigned int ptmap_len;
 	uint32_t x_osmo_ign;
+	bool param_present;
+	struct mgcp_codec_param param;
 };
 
 void mgcp_client_conf_init(struct mgcp_client_conf *conf);
diff --git a/include/osmocom/mgcp_client/mgcp_client_fsm.h b/include/osmocom/mgcp_client/mgcp_client_fsm.h
index 716a6d4..dabfcca 100644
--- a/include/osmocom/mgcp_client/mgcp_client_fsm.h
+++ b/include/osmocom/mgcp_client/mgcp_client_fsm.h
@@ -51,6 +51,11 @@
 	/*! If left MGCP_CONN_NONE, use MGCP_CONN_RECV_ONLY or MGCP_CONN_RECV_SEND, depending on whether an audio RTP
 	 * address is set. If != MGCP_CONN_NONE, force this conn mode. */
 	enum mgcp_connection_mode conn_mode;
+
+	/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also
+	 * mgcp_common.h */
+	bool param_present;
+	struct mgcp_codec_param param;
 };
 
 struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
diff --git a/src/libosmo-mgcp-client/mgcp_client.c b/src/libosmo-mgcp-client/mgcp_client.c
index ead3512..88e5dab 100644
--- a/src/libosmo-mgcp-client/mgcp_client.c
+++ b/src/libosmo-mgcp-client/mgcp_client.c
@@ -1051,6 +1051,20 @@
 	}
 	rc += msgb_printf(msg, "\r\n");
 
+	/* Add optional codec parameters (fmtp) */
+	if (mgcp_msg->param_present) {
+		for (i = 0; i < mgcp_msg->codecs_len; i++) {
+			/* The following is only applicable for AMR */
+			if (mgcp_msg->codecs[i] != CODEC_AMR_8000_1 && mgcp_msg->codecs[i] != CODEC_AMRWB_16000_1)
+				   continue;
+			pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
+			if (mgcp_msg->param.amr_octet_aligned_present && mgcp_msg->param.amr_octet_aligned)
+				rc += msgb_printf(msg, "a=fmtp:%u octet-align=1\r\n", pt);
+			else if (mgcp_msg->param.amr_octet_aligned_present && !mgcp_msg->param.amr_octet_aligned)
+				rc += msgb_printf(msg, "a=fmtp:%u octet-align=0\r\n", pt);
+		}
+	}
+
 	for (i = 0; i < mgcp_msg->codecs_len; i++) {
 		pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
 		
diff --git a/src/libosmo-mgcp-client/mgcp_client_fsm.c b/src/libosmo-mgcp-client/mgcp_client_fsm.c
index 7c4e081..71f4310 100644
--- a/src/libosmo-mgcp-client/mgcp_client_fsm.c
+++ b/src/libosmo-mgcp-client/mgcp_client_fsm.c
@@ -114,11 +114,13 @@
 		.conn_mode = MGCP_CONN_RECV_ONLY,
 		.ptime = info->ptime,
 		.codecs_len = info->codecs_len,
-		.ptmap_len = info->ptmap_len
+		.ptmap_len = info->ptmap_len,
+		.param_present = info->param_present
 	};
 	osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN);
 	memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs));
 	memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap));
+	memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param));
 
 	if (info->x_osmo_ign) {
 		mgcp_msg->x_osmo_ign = info->x_osmo_ign;
@@ -156,11 +158,13 @@
 		.audio_port = mgcp_ctx->conn_peer_local.port,
 		.ptime = mgcp_ctx->conn_peer_local.ptime,
 		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len,
-		.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len
+		.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len,
+		.param_present = mgcp_ctx->conn_peer_local.param_present
 	};
 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
 	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
 	memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap));
+	memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param));
 
 	set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local);
 
diff --git a/src/libosmo-mgcp/mgcp_codec.c b/src/libosmo-mgcp/mgcp_codec.c
index 55be554..933284d 100644
--- a/src/libosmo-mgcp/mgcp_codec.c
+++ b/src/libosmo-mgcp/mgcp_codec.c
@@ -100,8 +100,8 @@
 }
 
 /* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
-static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
-		     int payload_type, const char *audio_name, unsigned int pt_offset)
+static int codec_set(void *ctx, struct mgcp_rtp_codec *codec, int payload_type, const char *audio_name,
+		     unsigned int pt_offset, struct mgcp_codec_param *param)
 {
 	int rate;
 	int channels;
@@ -219,6 +219,13 @@
 		}
 	}
 
+	/* Copy over optional codec parameters */
+	if (param) {
+		codec->param = *param;
+		codec->param_present = true;
+	} else
+		codec->param_present = false;
+
 	return 0;
 error:
 	/* Make sure we leave a clean codec entry on error. */
@@ -233,8 +240,9 @@
  *  \param[out] conn related rtp-connection.
  *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
  *  \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
+ *  \param[in] param optional codec parameters (set to NULL when unused).
  *  \returns 0 on success, -EINVAL on failure. */
-int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
+int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, struct mgcp_codec_param *param)
 {
 	int rc;
 
@@ -244,7 +252,7 @@
 		return -EINVAL;
 
 	rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
-		       conn->end.codecs_assigned);
+		       conn->end.codecs_assigned, param);
 	if (rc != 0)
 		return -EINVAL;
 
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index e4fd7c0..2c6c571 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -34,6 +34,7 @@
 #include <osmocom/core/socket.h>
 #include <osmocom/core/byteswap.h>
 #include <osmocom/netif/rtp.h>
+#include <osmocom/netif/amr.h>
 #include <osmocom/mgcp/mgcp.h>
 #include <osmocom/mgcp/mgcp_common.h>
 #include <osmocom/mgcp/mgcp_internal.h>
@@ -684,6 +685,86 @@
 	}
 }
 
+/* For AMR RTP two framing modes are defined RFC3267. There is a bandwith
+ * efficient encoding scheme where all fields are packed together one after
+ * another and an octet aligned mode where all fields are aligned to octet
+ * boundaries. This function is used to convert between the two modes */
+static int amr_oa_bwe_convert(struct mgcp_endpoint *endp, char *data, int *len,
+			      bool target_is_oa)
+{
+	/* NOTE: *data has an overall length of RTP_BUF_SIZE, so there is
+	 * plenty of space available to store the slightly larger, converted
+	 * data */
+
+	struct rtp_hdr *rtp_hdr;
+	unsigned int payload_len;
+	int rc;
+
+	OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
+	rtp_hdr = (struct rtp_hdr *)data;
+
+	payload_len = *len - sizeof(struct rtp_hdr);
+
+	if (osmo_amr_is_oa(rtp_hdr->data, payload_len)) {
+		if (!target_is_oa)
+			/* Input data is oa an target format is bwe
+			 * ==> convert */
+			rc = osmo_amr_oa_to_bwe(rtp_hdr->data, payload_len);
+		else
+			/* Input data is already bew, but we accept it anyway
+			 * ==> no conversion needed */
+			rc = payload_len;
+	} else {
+		if (target_is_oa)
+			/* Input data is bwe an target format is oa
+			 * ==> convert */
+			rc = osmo_amr_bwe_to_oa(rtp_hdr->data, payload_len,
+						RTP_BUF_SIZE);
+		else
+			/* Input data is already oa, but we accept it anyway
+			 * ==> no conversion needed */
+			rc = payload_len;
+	}
+	if (rc < 0) {
+		LOGP(DRTP, LOGL_ERROR,
+		     "endpoint:0x%x AMR RTP packet conversion failed\n",
+		     ENDPOINT_NUMBER(endp));
+		return -EINVAL;
+	}
+
+	*len = rc + sizeof(struct rtp_hdr);
+
+	return 0;
+}
+
+/* Check if a conversion between octet-aligned and bandwith-efficient mode is
+ * indicated. */
+static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec)
+{
+	if (codec->param_present == false)
+		return false;
+	if (!codec->param.amr_octet_aligned_present)
+		return false;
+	if (strcmp(codec->subtype_name, "AMR") != 0)
+		return false;
+	return true;
+}
+
+
+/* Check if a given RTP with AMR payload for octet-aligned mode */
+static bool amr_oa_check(char *data, int len)
+{
+	struct rtp_hdr *rtp_hdr;
+	unsigned int payload_len;
+
+	OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
+	rtp_hdr = (struct rtp_hdr *)data;
+
+	payload_len = len - sizeof(struct rtp_hdr);
+
+	return osmo_amr_is_oa(rtp_hdr->data, payload_len);
+}
+
 /* Forward data to a debug tap. This is debug function that is intended for
  * debugging the voice traffic with tools like gstreamer */
 static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
@@ -792,7 +873,11 @@
 				mgcp_patch_and_count(endp, rtp_state, rtp_end,
 						     addr, buf, buflen);
 
-			if (rtp_end->rfc5993_hr_convert
+			if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) {
+				amr_oa_bwe_convert(endp, buf, &buflen,
+						   conn_dst->end.codec->param.amr_octet_aligned);
+			}
+			else if (rtp_end->rfc5993_hr_convert
 			    && strcmp(conn_src->end.codec->subtype_name,
 				      "GSM-HR-08") == 0)
 				rfc5993_hr_convert(endp, buf, &buflen);
@@ -1302,6 +1387,15 @@
 				     len, conn_src, conn_src);
 	}
 
+	/* If AMR is configured for the ingress connection a conversion of the
+	 * framing mode (octet-aligned vs. bandwith-efficient is explicitly
+	 * define, then we check if the incoming payload matches that
+	 * expectation. */
+	if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) {
+		if (amr_oa_check(buf, len) != conn_src->end.codec->param.amr_octet_aligned)
+			return -1;
+	}
+
 	/* Execute endpoint specific implementation that handles the
 	 * dispatching of the RTP data */
 	return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index 82db02f..4121b9f 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -724,7 +724,7 @@
 		/* When no SDP is available, we use the codec information from
 		 * the local connection options (if present) */
 		mgcp_codec_reset_all(conn);
-		rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
+		rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
 		if (rc != 0)
 			goto error;
 	}
@@ -735,7 +735,7 @@
 		 * than it makes sense to pick a sane default: (payload-type 0,
 		 * PCMU), see also: OS#2658 */
 		mgcp_codec_reset_all(conn);
-		rc = mgcp_codec_add(conn, 0, NULL);
+		rc = mgcp_codec_add(conn, 0, NULL, NULL);
 		if (rc != 0)
 			goto error;
 	}
diff --git a/src/libosmo-mgcp/mgcp_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c
index 3287cd1..02b9695 100644
--- a/src/libosmo-mgcp/mgcp_sdp.c
+++ b/src/libosmo-mgcp/mgcp_sdp.c
@@ -30,9 +30,9 @@
 
 #include <errno.h>
 
-/* A struct to store intermediate parsing results. The function
- * mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
- * codec information. */
+/* Two structs to store intermediate parsing results. The function
+ * mgcp_parse_sdp_data() is using the following two structs as temporary
+ * storage for parsing the SDP codec information. */
 struct sdp_rtp_map {
 	/* the type */
 	int payload_type;
@@ -44,6 +44,11 @@
 	int rate;
 	int channels;
 };
+struct sdp_fmtp_param {
+	int payload_type;
+	struct mgcp_codec_param param;
+};
+
 
 /* Helper function to extrapolate missing codec parameters in a codec mao from
  * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
@@ -168,6 +173,92 @@
 	return -EINVAL;
 }
 
+/* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */
+static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp)
+{
+	char *str;
+	char *str_ptr;
+	char *param_str;
+	unsigned int pt;
+	unsigned int count = 0;
+	char delimiter;
+	unsigned int amr_octet_aligned;
+
+	memset(fmtp_param, 0, sizeof(*fmtp_param));
+
+	str = talloc_zero_size(ctx, strlen(sdp) + 1);
+	str_ptr = str;
+	strcpy(str_ptr, sdp);
+
+	/* Check if the input string begins with an fmtp token */
+	str_ptr = strstr(str_ptr, "fmtp:");
+	if (!str_ptr)
+		goto exit;
+	str_ptr += 5;
+
+	/* Extract payload type */
+	if (sscanf(str_ptr, "%u ", &pt) != 1)
+		goto error;
+	fmtp_param->payload_type = pt;
+
+	/* Advance pointer to the beginning of the parameter section and
+	 * tokenize string */
+	str_ptr = strstr(str_ptr, " ");
+	if (!str_ptr)
+		goto error;
+	str_ptr++;
+
+	param_str = strtok(str_ptr, " ");
+	if (!param_str)
+		goto exit;
+
+	while (1) {
+		/* Make sure that we don't get trapped in an endless loop */
+		if (count > 256)
+			goto error;
+
+		/* Chop off delimiters ';' at the end */
+		delimiter = str_ptr[strlen(str_ptr) - 1];
+		if (delimiter == ';' || delimiter == ',')
+			str_ptr[strlen(str_ptr) - 1] = '\0';
+
+		/* AMR octet aligned parameter */
+		if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
+			fmtp_param->param.amr_octet_aligned_present = true;
+			fmtp_param->param.amr_octet_aligned = false;
+			if (amr_octet_aligned == 1)
+				fmtp_param->param.amr_octet_aligned = true;
+
+		}
+
+		param_str = strtok(NULL, " ");
+		if (!param_str)
+			break;
+		count++;
+	}
+
+exit:
+	talloc_free(str);
+	return 0;
+error:
+	talloc_free(str);
+	return -EINVAL;
+}
+
+/* Pick optional fmtp parameters by payload type, if there are no fmtp
+ * parameters, a nullpointer is returned */
+static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
+{
+	unsigned int i;
+
+	for (i = 0; i < fmtp_params_len; i++) {
+		if (fmtp_params[i].payload_type == pt)
+			return &fmtp_params[i].param;
+	}
+
+	return NULL;
+}
+
 /*! Analyze SDP input string.
  *  \param[in] endp trunk endpoint.
  *  \param[out] conn associated rtp connection.
@@ -181,6 +272,9 @@
 {
 	struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
 	unsigned int codecs_used = 0;
+	struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
+	unsigned int fmtp_used = 0;
+	struct mgcp_codec_param *codec_param;
 	char *line;
 	unsigned int i;
 	void *tmp_ctx = talloc_new(NULL);
@@ -225,6 +319,14 @@
 				rtp->maximum_packet_time = ptime2;
 				break;
 			}
+
+			if (strncmp("a=fmtp:", line, 6) == 0) {
+				rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
+				if (rc >= 0)
+					fmtp_used++;
+				break;
+			}
+
 			break;
 		case 'm':
 			rc = sscanf(line, "m=audio %d RTP/AVP", &port);
@@ -266,7 +368,8 @@
 
 	/* Store parsed codec information */
 	for (i = 0; i < codecs_used; i++) {
-		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
+		codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
+		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
 		if (rc < 0)
 			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
 	}
@@ -334,6 +437,64 @@
 	return 0;
 }
 
+/* Add fmtp strings to sdp payload */
+static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
+		    const char *fmtp_extra)
+{
+	unsigned int i;
+	int rc;
+	int fmtp_extra_pt = -1;
+	char *fmtp_extra_pars = "";
+
+	/* When no fmtp parameters ara available but an fmtp extra string
+	 * is configured, just add the fmtp extra string */
+	if (fmtp_params_len == 0 && fmtp_extra) {
+		return msgb_printf(sdp, "%s\r\n", fmtp_extra);
+	}
+
+	/* When there is fmtp extra configured we dissect it in order to drop
+	 * in the configured extra parameters at the right place when
+	 * generating the fmtp strings. */
+	if (fmtp_extra) {
+		if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
+			fmtp_extra_pt = -1;
+
+		fmtp_extra_pars = strstr(fmtp_extra, " ");
+
+		if (!fmtp_extra_pars)
+			fmtp_extra_pars = "";
+		else
+			fmtp_extra_pars++;
+	}
+
+	for (i = 0; i < fmtp_params_len; i++) {
+		rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
+
+		/* Add amr octet align parameter */
+		if (fmtp_params[i].param.amr_octet_aligned_present) {
+			if (fmtp_params[i].param.amr_octet_aligned)
+				rc = msgb_printf(sdp, " octet-align=1");
+			else
+				rc = msgb_printf(sdp, " octet-align=0");
+			if (rc < 0)
+				return -EINVAL;
+		}
+
+		/* Append extra parameters from fmtp extra */
+		if (fmtp_params[i].payload_type == fmtp_extra_pt) {
+			rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
+			if (rc < 0)
+				return -EINVAL;
+		}
+
+		rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type);
+		if (rc < 0)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 /*! Generate SDP response string.
  *  \param[in] endp trunk endpoint.
  *  \param[in] conn associated rtp connection.
@@ -348,8 +509,11 @@
 	const char *fmtp_extra;
 	const char *audio_name;
 	int payload_type;
+	struct sdp_fmtp_param fmtp_param;
 	int rc;
 	int payload_types[1];
+	struct sdp_fmtp_param fmtp_params[1];
+        unsigned int fmtp_params_len = 0;
 
 	OSMO_ASSERT(endp);
 	OSMO_ASSERT(conn);
@@ -387,12 +551,15 @@
 				goto buffer_too_small;
 		}
 
-		if (fmtp_extra) {
-			rc = msgb_printf(sdp, "%s\r\n", fmtp_extra);
-
-			if (rc < 0)
-				goto buffer_too_small;
+		if (codec->param_present) {
+			fmtp_param.payload_type = payload_type;
+			fmtp_param.param = codec->param;
+			fmtp_params[0] = fmtp_param;
+			fmtp_params_len = 1;
 		}
+		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
+		if (rc < 0)
+			goto buffer_too_small;
 	}
 	if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
 		rc = msgb_printf(sdp, "a=ptime:%u\r\n",
diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c
index 78ddcdc..be45598 100644
--- a/tests/mgcp/mgcp_test.c
+++ b/tests/mgcp/mgcp_test.c
@@ -467,6 +467,34 @@
 	"M: recvonly\r\n" \
 	"C: 2\r\n"
 
+#define CRCX_AMR_WITH_FMTP \
+	"CRCX 2 7 at mgw MGCP 1.0\r\n" \
+	"M: recvonly\r\n" \
+	"C: 2\r\n" \
+	"X\r\n" \
+	"L: p:20\r\n" \
+	"\r\n" \
+	"v=0\r\n" \
+	"c=IN IP4 123.12.12.123\r\n" \
+	"m=audio 5904 RTP/AVP 111\r\n" \
+	"a=rtpmap:111 AMR/8000/1\r\n" \
+	"a=ptime:20\r\n" \
+	"a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
+
+#define CRCX_AMR_WITH_FMTP_RET \
+	"200 2 OK\r\n" \
+	"I: %s\r\n" \
+	"\r\n" \
+	"v=0\r\n" \
+	"o=- %s 23 IN IP4 0.0.0.0\r\n" \
+	"s=-\r\n" \
+	"c=IN IP4 0.0.0.0\r\n" \
+	"t=0 0\r\n" \
+	"m=audio 16012 RTP/AVP 111\r\n" \
+	"a=rtpmap:111 AMR/8000/1\r\n" \
+	"a=fmtp:111 octet-align=1\r\n" \
+	"a=ptime:20\r\n"
+
 #define CRCX_NO_LCO_NO_SDP_RET \
 	"200 2 OK\r\n" \
 	"I: %s\r\n" \
@@ -517,6 +545,7 @@
 	{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
 	{"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
 	{"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
+	{"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
 };
 
 static const struct mgcp_test retransmit[] = {
@@ -1246,7 +1275,7 @@
 
 	rtp = &conn->end;
 
-	OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
+	OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
 	rtp->codec = &rtp->codecs[0];
 
 	for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
diff --git a/tests/mgcp/mgcp_test.ok b/tests/mgcp/mgcp_test.ok
index 28e9aad..d21eaa1 100644
--- a/tests/mgcp/mgcp_test.ok
+++ b/tests/mgcp/mgcp_test.ok
@@ -459,6 +459,30 @@
 Testing CRCX
 creating message from statically defined input:
 ---------8<---------
+CRCX 2 7 at mgw MGCP 1.0
+M: recvonly
+C: 2
+X
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 111
+a=rtpmap:111 AMR/8000/1
+a=ptime:20
+a=fmtp:111 mode-change-capability=2; octet-align=1
+
+---------8<---------
+checking response:
+using message with patched conn_id for comparison
+Response matches our expectations.
+(response contains a connection id)
+Dummy packets: 2
+
+================================================
+Testing CRCX
+creating message from statically defined input:
+---------8<---------
 CRCX 2 1 at mgw MGCP 1.0
 M: recvonly
 C: 2

-- 
To view, visit https://gerrit.osmocom.org/13159
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I622c01874b25f5049d4f59eb8157e0ea3cbe16ba
Gerrit-Change-Number: 13159
Gerrit-PatchSet: 3
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190319/2d5f7636/attachment.html>


More information about the gerrit-log mailing list