Add option to GSM HR frames to RFC5593 representation

There are different specifications around on how a GSM-HR frame should
be encapsulated into an RTP packet. RFC5593 specifies a ToC (Table of
Contents) byte to be prepended in front of the payload data.

The two formats can be distinguished easily by their length. Then the
data can be formatted into the corresponding opposite format and vice
versa.

- Add new VTY rtp-patch options
- Add conversion function

Change-Id: Iceef19e5619f8c92dfa7c8cdecb2e9b15f0a11a1
Related: OS#3807
diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h
index 7597af8..5886a65 100644
--- a/include/osmocom/mgcp/mgcp.h
+++ b/include/osmocom/mgcp/mgcp.h
@@ -190,6 +190,7 @@
 	/* RTP patching */
 	int force_constant_ssrc; /* 0: don't, 1: once */
 	int force_aligned_timing;
+	bool rfc5993_hr_convert;
 
 	/* spec handling */
 	int force_realloc;
diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h
index 14c5f11..a6239c2 100644
--- a/include/osmocom/mgcp/mgcp_internal.h
+++ b/include/osmocom/mgcp/mgcp_internal.h
@@ -128,6 +128,7 @@
 	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
 	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
 	int force_aligned_timing;
+	bool rfc5993_hr_convert;
 
 	/* Each end has a separate socket for RTP and RTCP */
 	struct osmo_fd rtp;
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index 2c86f8f..33738bc 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -43,6 +43,7 @@
 #include <osmocom/mgcp/mgcp_endp.h>
 #include <osmocom/mgcp/mgcp_codec.h>
 #include <osmocom/mgcp/debug.h>
+#include <osmocom/codec/codec.h>
 
 
 #define RTP_SEQ_MOD		(1 << 16)
@@ -651,6 +652,40 @@
 #endif
 }
 
+/* There are different dialects used to format and transfer voice data. When
+ * the receiving end expects GSM-HR data to be formated after RFC 5993, this
+ * function is used to convert between RFC 5993 and TS 101318, which we normally
+ * use. */
+static void rfc5993_hr_convert(struct mgcp_endpoint *endp, char *data, int *len)
+{
+	/* 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;
+
+	OSMO_ASSERT(*len >= sizeof(struct rtp_hdr));
+	rtp_hdr = (struct rtp_hdr *)data;
+
+	if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
+		/* TS 101318 encoding => RFC 5993 encoding */
+		memmove(rtp_hdr->data + 1, rtp_hdr->data, GSM_HR_BYTES);
+		rtp_hdr->data[0] = 0x00;
+		(*len) += 1;
+
+	} else if (*len == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
+		/* RFC 5993 encoding => TS 101318 encoding */
+		memmove(rtp_hdr->data, rtp_hdr->data + 1, GSM_HR_BYTES);
+		(*len) -= 1;
+	} else {
+		/* It is possible that multiple payloads occur in one RTP
+		 * packet. This is not supported yet. */
+		LOGP(DRTP, LOGL_ERROR,
+		     "endpoint:0x%x cannot figure out how to convert RTP packet\n",
+		     ENDPOINT_NUMBER(endp));
+	}
+}
+
 /* 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,
@@ -758,6 +793,12 @@
 			if (addr)
 				mgcp_patch_and_count(endp, rtp_state, rtp_end,
 						     addr, buf, buflen);
+
+			if (rtp_end->rfc5993_hr_convert
+			    && strcmp(conn_src->end.codec->subtype_name,
+				      "GSM-HR-08") == 0)
+				rfc5993_hr_convert(endp, buf, &buflen);
+
 			LOGP(DRTP, LOGL_DEBUG,
 			     "endpoint:0x%x process/send to %s %s "
 			     "rtp_port:%u rtcp_port:%u\n",
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index 9f95ea4..82db02f 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -652,6 +652,7 @@
 
 	rtp->force_aligned_timing = tcfg->force_aligned_timing;
 	rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
+	rtp->rfc5993_hr_convert = tcfg->rfc5993_hr_convert;
 
 	LOGP(DLMGCP, LOGL_DEBUG,
 	     "Configuring RTP endpoint: local port %d%s%s\n",
diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c
index ef63b04..a47376b 100644
--- a/src/libosmo-mgcp/mgcp_vty.c
+++ b/src/libosmo-mgcp/mgcp_vty.c
@@ -37,6 +37,8 @@
 #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
 #define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
 #define RTP_KEEPALIVE_STR "Send dummy UDP packet to net RTP destination\n"
+#define RTP_TS101318_RFC5993_CONV_STR "Convert GSM-HR from TS101318 to RFC5993 and vice versa\n"
+
 
 static struct mgcp_config *g_cfg = NULL;
 
@@ -96,13 +98,17 @@
 	else
 		vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
 	if (g_cfg->trunk.force_constant_ssrc
-	    || g_cfg->trunk.force_aligned_timing) {
+	    || g_cfg->trunk.force_aligned_timing
+	    || g_cfg->trunk.rfc5993_hr_convert) {
 		vty_out(vty, "  %srtp-patch ssrc%s",
 			g_cfg->trunk.force_constant_ssrc ? "" : "no ",
 			VTY_NEWLINE);
 		vty_out(vty, "  %srtp-patch timestamp%s",
 			g_cfg->trunk.force_aligned_timing ? "" : "no ",
 			VTY_NEWLINE);
+		vty_out(vty, "  %srtp-patch rfc5993hr%s",
+			g_cfg->trunk.rfc5993_hr_convert ? "" : "no ",
+			VTY_NEWLINE);
 	} else
 		vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
 	if (g_cfg->trunk.audio_payload != -1)
@@ -722,11 +728,28 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_mgcp_patch_rtp_rfc5993hr,
+      cfg_mgcp_patch_rtp_rfc5993hr_cmd,
+      "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
+{
+	g_cfg->trunk.rfc5993_hr_convert = true;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_patch_rtp_rfc5993hr,
+      cfg_mgcp_no_patch_rtp_rfc5993hr_cmd,
+      "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
+{
+	g_cfg->trunk.rfc5993_hr_convert = false;
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_mgcp_no_patch_rtp,
       cfg_mgcp_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
 {
 	g_cfg->trunk.force_constant_ssrc = 0;
 	g_cfg->trunk.force_aligned_timing = 0;
+	g_cfg->trunk.rfc5993_hr_convert = false;
 	return CMD_SUCCESS;
 }
 
@@ -823,13 +846,17 @@
 			vty_out(vty, "  rtcp-omit%s", VTY_NEWLINE);
 		else
 			vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
-		if (trunk->force_constant_ssrc || trunk->force_aligned_timing) {
+		if (trunk->force_constant_ssrc || trunk->force_aligned_timing
+		    || g_cfg->trunk.rfc5993_hr_convert) {
 			vty_out(vty, "  %srtp-patch ssrc%s",
 				trunk->force_constant_ssrc ? "" : "no ",
 				VTY_NEWLINE);
 			vty_out(vty, "  %srtp-patch timestamp%s",
 				trunk->force_aligned_timing ? "" : "no ",
 				VTY_NEWLINE);
+			vty_out(vty, "  %srtp-patch rfc5993hr%s",
+				trunk->rfc5993_hr_convert ? "" : "no ",
+				VTY_NEWLINE);
 		} else
 			vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
 		if (trunk->audio_fmtp_extra)
@@ -995,12 +1022,31 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_trunk_patch_rtp_rfc5993hr,
+      cfg_trunk_patch_rtp_rfc5993hr_cmd,
+      "rtp-patch rfc5993hr", RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->rfc5993_hr_convert = true;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_patch_rtp_rfc5993hr,
+      cfg_trunk_no_patch_rtp_rfc5993hr_cmd,
+      "no rtp-patch rfc5993hr", NO_STR RTP_PATCH_STR RTP_TS101318_RFC5993_CONV_STR)
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->rfc5993_hr_convert = false;
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_trunk_no_patch_rtp,
       cfg_trunk_no_patch_rtp_cmd, "no rtp-patch", NO_STR RTP_PATCH_STR)
 {
 	struct mgcp_trunk_config *trunk = vty->index;
 	trunk->force_constant_ssrc = 0;
 	trunk->force_aligned_timing = 0;
+	trunk->rfc5993_hr_convert = false;
 	return CMD_SUCCESS;
 }
 
@@ -1400,6 +1446,8 @@
 	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_rfc5993hr_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_rfc5993hr_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd);
@@ -1431,6 +1479,8 @@
 	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_rfc5993hr_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_rfc5993hr_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd);