diff mgw/pstn2gsm.c @ 95:f280328e7e2e

themwi-mgw: initial implementation of PSTN to GSM forwarding
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 25 Sep 2022 19:17:44 -0800
parents
children f24bbfd23c9d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mgw/pstn2gsm.c	Sun Sep 25 19:17:44 2022 -0800
@@ -0,0 +1,140 @@
+/*
+ * In this module we implement our RTP gateway function
+ * in the PSTN to GSM direction.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <gsm.h>	/* libgsm dependency */
+#include "../include/tmgw_ctrl.h"
+#include "../include/tmgw_const.h"
+#include "struct.h"
+#include "select.h"
+#include "int_defs.h"
+
+extern uint16_t pcmu_decode_table[256];
+extern uint16_t pcma_decode_table[256];
+
+#define	ERR_WRONG_UDP_SRC	0x0001
+#define	ERR_BAD_RTP_PACKET	0x0002
+#define	ERR_SSRC_CHANGE		0x0004
+#define	ERR_SEQ_BREAK		0x0008
+#define	ERR_TSTAMP_BREAK	0x0010
+
+void
+pstn2gsm_rtp_in(in_fd, ep)
+	struct endpoint *ep;
+{
+	struct rtp_packet pkt;
+	struct sockaddr_in sin_from;
+	socklen_t addrlen;
+	uint16_t *pcm_dec_table;
+	int16_t seq_delta;
+	int32_t ts_delta;
+	int16_t pcm_samples[SAMPLES_PER_FRAME];
+	unsigned n;
+	int rc, m_out;
+
+	addrlen = sizeof(struct sockaddr_in);
+	rc = recvfrom(in_fd, &pkt, sizeof pkt, 0,
+			(struct sockaddr *) &sin_from, &addrlen);
+	if (rc < 0)
+		return;
+	if (sin_from.sin_addr.s_addr != ep->rtp_pstn.remote_addr.sin_addr.s_addr
+	    || sin_from.sin_port != ep->rtp_pstn.remote_addr.sin_port) {
+		if (!(ep->p2g_err_flags & ERR_WRONG_UDP_SRC)) {
+			syslog(LOG_ERR,
+				"PSTN RTP ep got UDP packet from wrong source");
+			ep->p2g_err_flags |= ERR_WRONG_UDP_SRC;
+		}
+		return;
+	}
+	if (rc != RTP_PACKET_SIZE_PSTN) {
+bad_rtp_pkt:	if (!(ep->p2g_err_flags & ERR_BAD_RTP_PACKET)) {
+			syslog(LOG_ERR, "Rx bad RTP packet on PSTN side");
+			ep->p2g_err_flags |= ERR_BAD_RTP_PACKET;
+		}
+		return;
+	}
+	if (pkt.v_p_x_cc != 0x80)
+		goto bad_rtp_pkt;
+	switch (pkt.m_pt & 0x7F) {
+	case PSTN_CODEC_PCMU:
+		pcm_dec_table = pcmu_decode_table;
+		break;
+	case PSTN_CODEC_PCMA:
+		pcm_dec_table = pcma_decode_table;
+		break;
+	default:
+		goto bad_rtp_pkt;
+	}
+	if (ep->p2g_state && pkt.ssrc != ep->p2g_ssrc) {
+		if (!(ep->p2g_err_flags & ERR_SSRC_CHANGE)) {
+			syslog(LOG_ERR, "PSTN RTP stream changed SSRC");
+			ep->p2g_err_flags |= ERR_SSRC_CHANGE;
+		}
+		ep->p2g_state = 0;
+	}
+	if (ep->p2g_state) {
+		seq_delta = ntohs(pkt.seq) - ep->p2g_last_seq;
+		ts_delta = ntohl(pkt.tstamp) - ep->p2g_last_ts;
+		if (seq_delta <= 0)
+			return;	/* discard old or duplicate */
+		if (seq_delta != 1) {
+			if (!(ep->p2g_err_flags & ERR_SEQ_BREAK)) {
+				syslog(LOG_ERR, "PSTN RTP stream seq break");
+				ep->p2g_err_flags |= ERR_SEQ_BREAK;
+			}
+			m_out = 1;
+		} else if (ts_delta != SAMPLES_PER_FRAME) {
+			if (!(ep->p2g_err_flags & ERR_TSTAMP_BREAK)) {
+				syslog(LOG_ERR, "PSTN RTP stream tstamp break");
+				ep->p2g_err_flags |= ERR_TSTAMP_BREAK;
+			}
+			m_out = 1;
+		} else
+			m_out = 0;
+	} else
+		m_out = 1;
+	ep->p2g_state = 1;
+	ep->p2g_ssrc = pkt.ssrc;
+	ep->p2g_last_ts = htonl(pkt.tstamp);
+	ep->p2g_last_seq = htons(pkt.seq);
+	/* actual transcoding and forwarding */
+	for (n = 0; n < SAMPLES_PER_FRAME; n++)
+		pcm_samples[n] = pcm_dec_table[pkt.payload[n]];
+	pkt.m_pt = ep->gsm_payload_type;
+	if (m_out)
+		pkt.m_pt |= 0x80;
+	switch (ep->gsm_payload_msg_type) {
+	case GSM_TCHF_FRAME:
+		gsm_encode(ep->gsm_encoder_state, pcm_samples, pkt.payload);
+		n = RTP_PACKET_SIZE_GSM_FR;
+		break;
+	}
+	addrlen = sizeof(struct sockaddr_in);
+	sendto(ep->rtp_gsm.rtp_fd, &pkt, n, 0,
+		(struct sockaddr *) &ep->rtp_gsm.remote_addr, addrlen);
+}
+
+pstn2gsm_init(ep)
+	struct endpoint *ep;
+{
+	switch (ep->gsm_payload_msg_type) {
+	case GSM_TCHF_FRAME:
+		ep->gsm_encoder_state = gsm_create();
+		if (!ep->gsm_encoder_state)
+			return TMGW_RESP_ERR_RSRC;
+		break;
+	}
+	select_handlers[ep->rtp_pstn.rtp_fd] = pstn2gsm_rtp_in;
+	return TMGW_RESP_OK;
+}