view mgw/pstn2gsm.c @ 170:a6eb2de277f6

mgw: massive simplification for continuous RTP stream from BTS
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 20 Nov 2022 01:58:47 -0800
parents f062c32a5116
children c985c33baeac
line wrap: on
line source

/*
 * In this module we implement our RTP gateway function
 * in the PSTN to GSM direction.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.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 = ntohl(pkt.tstamp);
	ep->p2g_last_seq = ntohs(pkt.seq);
	/* actual transcoding and forwarding */
	if (!(ep->fwd_mode & TMGW_FWD_ENABLE_PSTN2GSM)) {
		ep->p2g_drop_flag = 1;
		return;
	}
	if (ep->p2g_drop_flag) {
		ep->p2g_drop_flag = 0;
		m_out = 1;
	}
	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;
	pkt.seq = htons(++ep->p2g_out_seq);
	switch (ep->gsm_payload_msg_type) {
	case GSM_TCHF_FRAME:
		gsm_encode(ep->gsm_encoder_state, pcm_samples, pkt.payload);
		break;
	}
	addrlen = sizeof(struct sockaddr_in);
	sendto(ep->rtp_gsm.rtp_fd, &pkt, ep->gsm_rtp_pkt_size, 0,
		(struct sockaddr *) &ep->rtp_gsm.remote_addr, addrlen);
}

pstn2gsm_init(ep)
	struct endpoint *ep;
{
	if (ep->gsm_encoder_state)
		return TMGW_RESP_OK;
	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;
}