view test-voice/rtp_tx.c @ 17:830af7de3403

sipout-test-voice: disallow empty files for play command
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 13 May 2024 22:31:24 -0800
parents 71f01a834820
children
line wrap: on
line source

/*
 * In this module we implement outgoing RTP stream generation.
 */

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.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 <unistd.h>
#include "../include/tmgw_const.h"
#include "../include/rtp_defs.h"
#include "../libutil/osmo_bits.h"

extern struct sockaddr_in rtp_local_addr, rtp_remote_addr;
extern int rtp_udp_fd, rtcp_udp_fd, pcma_selected;
extern struct timeval cur_event_time;

static const uint8_t dmw_alaw[8] =
	{0x34, 0x21, 0x21, 0x34, 0xB4, 0xA1, 0xA1, 0xB4};
static const uint8_t dmw_ulaw[8] =
	{0x1E, 0x0B, 0x0B, 0x1E, 0x9E, 0x8B, 0x8B, 0x9E};

static uint32_t rtp_ssrc;
static uint32_t rtp_out_ts;
static uint16_t rtp_out_seq;

static uint8_t pcm_fill_octet;
static int dmw_active;

static uint8_t *play_buffer;
static unsigned play_buf_nframes, play_buf_ptr;
static int play_loop;

static uint16_t tfo_fill_buf[9], tfo_req_buf[7];
static uint16_t *is_out_ptr;
static unsigned is_out_count;
static int tfo_stop_req;

void
assign_rtpout_ssrc()
{
	rtp_ssrc = cur_event_time.tv_sec ^ cur_event_time.tv_usec ^ getpid();
}

void
init_pcm_fill_octet()
{
	if (pcma_selected)
		pcm_fill_octet = 0xD5;
	else
		pcm_fill_octet = 0xFF;
}

void
prepare_tfo_fill()
{
	tfo_fill_buf[0] = 0x15A;
	tfo_fill_buf[1] = 0x1A9;
	tfo_fill_buf[2] = 0x129;
	tfo_fill_buf[3] = 0x15A;
	tfo_fill_buf[4] = 0x1A9;
	tfo_fill_buf[5] = 0x129;
	tfo_fill_buf[6] = 0x15A;
	tfo_fill_buf[7] = 0x1A9;
	tfo_fill_buf[8] = 0x129;
}

static void
insert_is_msg(payload, word)
	uint8_t *payload;
	uint16_t word;
{
	ubit_t is_bits[10];
	uint8_t *bytep;
	unsigned n;

	uint16_to_bits(word, is_bits, 10);
	for (n = 0; n < 10; n++) {
		bytep = payload + n * 16;
		*bytep &= 0xFE;
		*bytep |= is_bits[n];
	}
}

static void
fill_with_dmw(buf)
	uint8_t *buf;
{
	const uint8_t *src;
	unsigned n;

	if (pcma_selected)
		src = dmw_alaw;
	else
		src = dmw_ulaw;
	for (n = 0; n < 20; n++) {
		memcpy(buf, src, 8);
		buf += 8;
	}
}

static void
fill_with_play(outbuf)
	uint8_t *outbuf;
{
	memcpy(outbuf, play_buffer + play_buf_ptr * 160, 160);
	play_buf_ptr++;
	if (play_buf_ptr < play_buf_nframes)
		return;
	if (play_loop)
		play_buf_ptr = 0;
	else {
		free(play_buffer);
		play_buffer = 0;
		printf("file play finished\n");
	}
}

void
generate_rtp_packet()
{
	struct rtp_packet pkt;
	socklen_t addrlen;

	pkt.v_p_x_cc = 0x80;
	pkt.m_pt = pcma_selected ? PSTN_CODEC_PCMA : PSTN_CODEC_PCMU;
	pkt.seq = htons(rtp_out_seq++);
	pkt.tstamp = htonl(rtp_out_ts);
	rtp_out_ts += 160;
	pkt.ssrc = rtp_ssrc;
	if (play_buffer)
		fill_with_play(pkt.payload);
	else if (dmw_active)
		fill_with_dmw(pkt.payload);
	else
		memset(pkt.payload, pcm_fill_octet, RTP_MAX_PAYLOAD);
	if (is_out_count) {
		insert_is_msg(pkt.payload, *is_out_ptr++);
		is_out_count--;
		if (!is_out_count && !tfo_stop_req) {
			is_out_ptr = tfo_req_buf;
			is_out_count = 7;
		}
	}
	addrlen = sizeof(struct sockaddr_in);
	sendto(rtp_udp_fd, &pkt, RTP_PACKET_SIZE_PSTN, 0,
		(struct sockaddr *) &rtp_remote_addr, addrlen);
}

void
set_pcm_fill_octet(oct)
	unsigned oct;
{
	pcm_fill_octet = oct;
}

void
send_tfo_req(sig, codec)
	unsigned sig, codec;
{
	tfo_req_buf[0] = 0x15A;
	tfo_req_buf[1] = 0x1A9;
	tfo_req_buf[2] = 0x05D;
	tfo_req_buf[3] = 0x14E;
	tfo_req_buf[4] = 0x14B;
	encode_tfo_ext_words(sig, codec, 0, tfo_req_buf + 5);
	is_out_ptr = tfo_fill_buf;
	is_out_count = 9;
	tfo_stop_req = 0;
}

void
stop_tfo_out()
{
	tfo_stop_req = 1;
}

void
cmd_dmw_on()
{
	dmw_active = 1;
}

void
cmd_dmw_off()
{
	dmw_active = 0;
}

void
play_file_cmdop(filename, loop)
	char *filename;
{
	int fd;
	struct stat st;

	if (play_buffer) {
		fprintf(stderr, "error: file play already in progress\n");
		return;
	}
	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		perror(filename);
		return;
	}
	fstat(fd, &st);
	if (!S_ISREG(st.st_mode)) {
		close(fd);
		fprintf(stderr, "error: %s is not a regular file\n", filename);
		return;
	}
	if (!st.st_size) {
		close(fd);
		fprintf(stderr, "error: %s is an empty file\n", filename);
		return;
	}
	if (st.st_size % 160) {
		close(fd);
		fprintf(stderr,
			"error: size of %s is not a multiple of 160 bytes\n",
			filename);
		return;
	}
	play_buffer = malloc(st.st_size);
	if (!play_buffer) {
		close(fd);
		fprintf(stderr, "unable to malloc buffer for %s\n", filename);
		return;
	}
	read(fd, play_buffer, st.st_size);
	close(fd);
	play_buf_nframes = st.st_size / 160;
	play_buf_ptr = 0;
	play_loop = loop;
}

void
play_file_offset(filename, offset)
	char *filename;
	unsigned offset;
{
	int fd;
	struct stat st;
	unsigned pre_offset;

	if (play_buffer) {
		fprintf(stderr, "error: file play already in progress\n");
		return;
	}
	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		perror(filename);
		return;
	}
	fstat(fd, &st);
	if (!S_ISREG(st.st_mode)) {
		close(fd);
		fprintf(stderr, "error: %s is not a regular file\n", filename);
		return;
	}
	if (!st.st_size) {
		close(fd);
		fprintf(stderr, "error: %s is an empty file\n", filename);
		return;
	}
	if (st.st_size % 160) {
		close(fd);
		fprintf(stderr,
			"error: size of %s is not a multiple of 160 bytes\n",
			filename);
		return;
	}
	play_buffer = malloc(st.st_size + 160);
	if (!play_buffer) {
		close(fd);
		fprintf(stderr, "unable to malloc buffer for %s\n", filename);
		return;
	}
	pre_offset = 160 - offset;
	memset(play_buffer, pcm_fill_octet, pre_offset);
	read(fd, play_buffer + pre_offset, st.st_size);
	close(fd);
	memset(play_buffer + pre_offset + st.st_size, pcm_fill_octet, offset);
	play_buf_nframes = st.st_size / 160 + 1;
	play_buf_ptr = 0;
	play_loop = 0;
}

void
play_file_stop()
{
	if (!play_buffer)
		return;
	free(play_buffer);
	play_buffer = 0;
}