view mgw/gsm2pstn.c @ 118:a4450ae8fd09

sip-manual-out UAC: use the new CSeq parsing function
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 29 Sep 2022 13:03:27 -0800
parents 3b3f07b112f3
children f062c32a5116
line wrap: on
line source

/*
 * In this module we implement our RTP gateway function
 * in the GSM to PSTN 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"

#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
gsm2pstn_rtp_in(in_fd, ep)
	struct endpoint *ep;
{
	struct rtp_packet pkt;
	struct sockaddr_in sin_from;
	socklen_t addrlen;
	int16_t seq_delta;
	int32_t ts_delta;
	int16_t pcm_samples[SAMPLES_PER_FRAME];
	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_gsm.remote_addr.sin_addr.s_addr
	    || sin_from.sin_port != ep->rtp_gsm.remote_addr.sin_port) {
		if (!(ep->g2p_err_flags & ERR_WRONG_UDP_SRC)) {
			syslog(LOG_ERR,
				"GSM RTP ep got UDP packet from wrong source");
			ep->g2p_err_flags |= ERR_WRONG_UDP_SRC;
		}
		return;
	}
	if (rc != ep->gsm_rtp_pkt_size) {
bad_rtp_pkt:	if (!(ep->g2p_err_flags & ERR_BAD_RTP_PACKET)) {
			syslog(LOG_ERR, "Rx bad RTP packet on GSM side");
			ep->g2p_err_flags |= ERR_BAD_RTP_PACKET;
		}
		return;
	}
	if (pkt.v_p_x_cc != 0x80)
		goto bad_rtp_pkt;
	if ((pkt.m_pt & 0x7F) != ep->gsm_payload_type)
		goto bad_rtp_pkt;
	if ((pkt.payload[0] & 0xF0) != ep->gsm_payload_magic)
		goto bad_rtp_pkt;
	if (ep->g2p_state && pkt.ssrc != ep->g2p_ssrc) {
		if (!(ep->g2p_err_flags & ERR_SSRC_CHANGE)) {
			syslog(LOG_ERR, "GSM RTP stream changed SSRC");
			ep->g2p_err_flags |= ERR_SSRC_CHANGE;
		}
		ep->g2p_state = 0;
	}
	if (ep->g2p_state) {
		seq_delta = ntohs(pkt.seq) - ep->g2p_last_seq;
		ts_delta = ntohl(pkt.tstamp) - ep->g2p_last_ts;
		if (seq_delta <= 0)
			return;	/* discard old or duplicate */
		if (seq_delta != 1) {
			if (!(ep->g2p_err_flags & ERR_SEQ_BREAK)) {
				syslog(LOG_ERR, "GSM RTP stream seq break");
				ep->g2p_err_flags |= ERR_SEQ_BREAK;
			}
			m_out = 1;
		} else {
			if (ts_delta == SAMPLES_PER_FRAME)
				m_out = 0;
			else if (ts_delta > 0 &&
				 ts_delta % SAMPLES_PER_FRAME == 0)
				m_out = 1;
			else {
				if (!(ep->g2p_err_flags & ERR_TSTAMP_BREAK)) {
					syslog(LOG_ERR,
						"GSM RTP stream tstamp break");
					ep->g2p_err_flags |= ERR_TSTAMP_BREAK;
				}
				m_out = 1;
			}
		}
	} else
		m_out = 1;
	ep->g2p_state = 1;
	ep->g2p_ssrc = pkt.ssrc;
	ep->g2p_last_ts = ntohl(pkt.tstamp);
	ep->g2p_last_seq = ntohs(pkt.seq);
	/* actual transcoding and forwarding */
	if (!(ep->fwd_mode & TMGW_FWD_ENABLE_GSM2PSTN)) {
		ep->g2p_drop_flag = 1;
		return;
	}
	if (ep->g2p_drop_flag) {
		ep->g2p_drop_flag = 0;
		m_out = 1;
	}
	switch (ep->gsm_payload_msg_type) {
	case GSM_TCHF_FRAME:
		gsm_decode(ep->gsm_decoder_state, pkt.payload, pcm_samples);
		break;
	}
	pkt.m_pt = ep->pstn_payload_type;
	if (m_out)
		pkt.m_pt |= 0x80;
	pkt.seq = htons(++ep->g2p_out_seq);
	g711_encode_frame(pcm_samples, pkt.payload, ep->pstn_payload_type);
	addrlen = sizeof(struct sockaddr_in);
	sendto(ep->rtp_pstn.rtp_fd, &pkt, RTP_PACKET_SIZE_PSTN, 0,
		(struct sockaddr *) &ep->rtp_pstn.remote_addr, addrlen);
}

gsm2pstn_init(ep)
	struct endpoint *ep;
{
	if (ep->gsm_decoder_state)
		return TMGW_RESP_OK;
	switch (ep->gsm_payload_msg_type) {
	case GSM_TCHF_FRAME:
		ep->gsm_decoder_state = gsm_create();
		if (!ep->gsm_decoder_state)
			return TMGW_RESP_ERR_RSRC;
		break;
	}
	select_handlers[ep->rtp_gsm.rtp_fd] = gsm2pstn_rtp_in;
	return TMGW_RESP_OK;
}