# HG changeset patch # User Mychaela Falconia # Date 1717907093 0 # Node ID 395c56969bc409dd2642c31dbe00173b2debcfc7 # Parent 0ec938ed530b57e256eb5a1ed78a161294e4e69e mtctest: implement guts of RTP play mechanism diff -r 0ec938ed530b -r 395c56969bc4 mtctest/Makefile --- 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 diff -r 0ec938ed530b -r 395c56969bc4 mtctest/main.c --- 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 #include #include +#include +#include #include #include #include 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; + } + } } } diff -r 0ec938ed530b -r 395c56969bc4 mtctest/rtp_play.c --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}