changeset 10:395c56969bc4

mtctest: implement guts of RTP play mechanism
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 09 Jun 2024 04:24:53 +0000
parents 0ec938ed530b
children aa2ba9b432af
files mtctest/Makefile mtctest/main.c mtctest/rtp_play.c
diffstat 3 files changed, 176 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/mtctest/Makefile	Sun Jun 09 03:37:44 2024 +0000
+++ b/mtctest/Makefile	Sun Jun 09 04:24:53 2024 +0000
@@ -1,7 +1,7 @@
 CPPFLAGS=-I${includedir}
 PROG=	themwi-test-mtc
-OBJS=	disconnect.o main.o rtp_sink.o setup.o sig_handler.o sock_conn.o \
-	user_cmd.o
+OBJS=	disconnect.o main.o rtp_play.o rtp_sink.o setup.o sig_handler.o \
+	sock_conn.o user_cmd.o
 LIBUTIL=../libutil/libutil.a
 
 include ../config.defs
--- a/mtctest/main.c	Sun Jun 09 03:37:44 2024 +0000
+++ b/mtctest/main.c	Sun Jun 09 04:24:53 2024 +0000
@@ -8,12 +8,15 @@
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
+#include <strings.h>
 #include <syslog.h>
 #include <unistd.h>
 #include <themwi/rtp/rtp_alloc_simple.h>
 
 extern int mtc_socket;
 extern struct rtp_alloc_simple rtp_info;
+extern int rtp_play_active;
 
 struct timeval cur_event_time;
 
@@ -24,7 +27,8 @@
 	extern char *optarg;
 	char *from;
 	fd_set fds;
-	int c, max_fd;
+	struct timeval next_rtp_out, timeout;
+	int c, max_fd, rtp_out_running;
 
 	from = 0;
 	while ((c = getopt(argc, argv, "f:")) != EOF) {
@@ -53,13 +57,33 @@
 		max_fd = rtp_info.gsm_rtp_fd;
 	if (rtp_info.gsm_rtcp_fd > mtc_socket)
 		max_fd = rtp_info.gsm_rtcp_fd;
+	rtp_out_running = 0;
 	for (;;) {
 		FD_ZERO(&fds);
 		FD_SET(0, &fds);
 		FD_SET(mtc_socket, &fds);
 		FD_SET(rtp_info.gsm_rtp_fd, &fds);
 		FD_SET(rtp_info.gsm_rtcp_fd, &fds);
-		c = select(max_fd+1, &fds, 0, 0, 0);
+		if (rtp_play_active) {
+			if (!rtp_out_running) {
+				printf("Starting RTP output\n");
+				bcopy(&cur_event_time, &next_rtp_out,
+					sizeof(struct timeval));
+				rtp_out_running = 1;
+			}
+			if (timercmp(&cur_event_time, &next_rtp_out, <))
+				timersub(&next_rtp_out, &cur_event_time,
+					 &timeout);
+			else
+				timerclear(&timeout);
+			c = select(max_fd+1, &fds, 0, 0, &timeout);
+		} else {
+			if (rtp_out_running) {
+				printf("Stopping RTP output\n");
+				rtp_out_running = 0;
+			}
+			c = select(max_fd+1, &fds, 0, 0, 0);
+		}
 		if (c < 0) {
 			if (errno == EINTR)
 				continue;
@@ -75,5 +99,13 @@
 			rtp_rx_select();
 		if (FD_ISSET(rtp_info.gsm_rtcp_fd, &fds))
 			rtcp_rx_select();
+		if (rtp_out_running && (c == 0)) {
+			generate_rtp_packet();
+			next_rtp_out.tv_usec += 20000;
+			if (next_rtp_out.tv_usec >= 1000000) {
+				next_rtp_out.tv_sec++;
+				next_rtp_out.tv_usec -= 1000000;
+			}
+		}
 	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mtctest/rtp_play.c	Sun Jun 09 04:24:53 2024 +0000
@@ -0,0 +1,140 @@
+/*
+ * In this module we implement RTP play command, emitting a test sequence
+ * of encoded speech frames toward the MS.
+ */
+
+#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 <arpa/inet.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <themwi/rtp/rtp_defs.h>
+#include <themwi/rtp/rtp_alloc_simple.h>
+
+extern struct rtp_alloc_simple rtp_info;
+extern int got_msc_rtp_info;
+extern struct sockaddr_storage msc_rtp_addr;
+extern uint8_t rtp_payload_type;
+extern struct timeval cur_event_time;
+
+int rtp_play_active;
+
+static uint8_t play_buffer[33*50*30];	/* max 30 s of GSM-FR play */
+
+static uint32_t rtp_ssrc;
+static uint32_t rtp_out_ts;
+static uint16_t rtp_out_seq;
+
+static unsigned play_frame_len;
+static unsigned play_buf_nframes, play_buf_ptr;
+static int play_loop;
+
+static void
+assign_rtpout_ssrc()
+{
+	rtp_ssrc = cur_event_time.tv_sec ^ cur_event_time.tv_usec ^ getpid();
+}
+
+static void
+fill_with_play(outbuf)
+	uint8_t *outbuf;
+{
+	memcpy(outbuf, play_buffer + play_buf_ptr * play_frame_len,
+		play_frame_len);
+	play_buf_ptr++;
+	if (play_buf_ptr < play_buf_nframes)
+		return;
+	play_buf_ptr = 0;
+	if (!play_loop)
+		rtp_play_active = 0;
+}
+
+void
+generate_rtp_packet()
+{
+	struct rtp_packet pkt;
+	socklen_t addrlen;
+
+	pkt.v_p_x_cc = 0x80;
+	pkt.m_pt = rtp_payload_type;
+	pkt.seq = htons(rtp_out_seq++);
+	pkt.tstamp = htonl(rtp_out_ts);
+	rtp_out_ts += 160;
+	pkt.ssrc = rtp_ssrc;
+	fill_with_play(pkt.payload);
+	addrlen = sizeof(struct sockaddr_in);
+	sendto(rtp_info.gsm_rtp_fd, &pkt, RTP_PACKET_HDR_SIZE + play_frame_len,
+		0, (struct sockaddr *) &msc_rtp_addr, addrlen);
+}
+
+void
+play_file_cmdop(filename, frame_len, loop)
+	unsigned frame_len;
+	char *filename;
+{
+	int fd;
+	struct stat st;
+
+	if (rtp_play_active) {
+		fprintf(stderr, "error: file play already in progress\n");
+		return;
+	}
+	if (!got_msc_rtp_info) {
+		fprintf(stderr, "error: no RTP info received from MSC\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 % frame_len) {
+		close(fd);
+		fprintf(stderr,
+			"error: size of %s is not a multiple of %u bytes\n",
+			filename, frame_len);
+		return;
+	}
+	if (st.st_size > sizeof play_buffer) {
+		close(fd);
+		fprintf(stderr,
+			"error: size of %s exceeds compiled-in buffer size\n",
+			filename);
+		return;
+	}
+	read(fd, play_buffer, st.st_size);
+	close(fd);
+	play_buf_nframes = st.st_size / frame_len;
+	play_buf_ptr = 0;
+	play_frame_len = frame_len;
+	play_loop = loop;
+	assign_rtpout_ssrc();
+	rtp_out_ts = 0;
+	rtp_out_seq = 0;
+	rtp_play_active = 1;
+}
+
+void
+play_file_stop()
+{
+	rtp_play_active = 0;
+}