mirror of
				http://www.pogo.org.uk/~mark/trx.git
				synced 2025-10-31 04:22:05 +01:00 
			
		
		
		
	trx based on libortp
This commit is contained in:
		
							parent
							
								
									c7af697019
								
							
						
					
					
						commit
						d2be2e03eb
					
				
							
								
								
									
										13
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Makefile
									
									
									
									
									
								
							| @ -2,23 +2,24 @@ | ||||
| 
 | ||||
| CFLAGS+=-MMD -Wall | ||||
| 
 | ||||
| LDLIBS_ASOUND?=-lasound | ||||
| LDLIBS_CELT?=-lcelt | ||||
| LDLIBS_ORTP?=-lortp | ||||
| 
 | ||||
| LDLIBS+=$(LDLIBS_ASOUND) $(LDLIBS_CELT) $(LDLIBS_ORTP) | ||||
| 
 | ||||
| .PHONY:		all clean | ||||
| 
 | ||||
| all:		rx tx | ||||
| 
 | ||||
| rx:		rx.o device.o jitter.o sched.o | ||||
| rx:		LDLIBS+=-lcelt -lasound | ||||
| rx:		rx.o device.o sched.o | ||||
| 
 | ||||
| tx:		tx.o device.o sched.o | ||||
| tx:		LDLIBS+=-lcelt -lasound | ||||
| 
 | ||||
| test-jitter:	test-jitter.o jitter.o | ||||
| 
 | ||||
| clean: | ||||
| 		rm -f *.o | ||||
| 		rm -f *.d | ||||
| 		rm -f tx | ||||
| 		rm -f rx | ||||
| 		rm -f test-jitter | ||||
| 
 | ||||
| -include *.d | ||||
|  | ||||
							
								
								
									
										10
									
								
								defaults.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								defaults.h
									
									
									
									
									
								
							| @ -4,11 +4,15 @@ | ||||
| #define DEFAULT_DEVICE "default" | ||||
| #define DEFAULT_BUFFER 16 | ||||
| 
 | ||||
| #define DEFAULT_SERVICE "1350" | ||||
| #define DEFAULT_FRAME 128 | ||||
| #define DEFAULT_ADDR "0.0.0.0" | ||||
| #define DEFAULT_PORT 1350 | ||||
| #define DEFAULT_FRAME 256 | ||||
| #define DEFAULT_JITTER 16 | ||||
| 
 | ||||
| #define DEFAULT_RATE 44100 | ||||
| #define DEFAULT_RATE 48000 | ||||
| #define DEFAULT_CHANNELS 2 | ||||
| #define DEFAULT_BITRATE 128 | ||||
| 
 | ||||
| #define DEFAULT_VERBOSE 1 | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										130
									
								
								jitter.c
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								jitter.c
									
									
									
									
									
								
							| @ -1,130 +0,0 @@ | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "jitter.h" | ||||
| 
 | ||||
| void jitbuf_init(struct jitbuf_t *jb) | ||||
| { | ||||
| 	size_t n; | ||||
| 
 | ||||
| 	for (n = 0; n < JITBUF_MAX; n++) | ||||
| 		jb->buf[n] = NULL; | ||||
| 
 | ||||
| 	jb->base = 0; | ||||
| 	jb->out = 0; | ||||
| 	jb->entries = 0; | ||||
| } | ||||
| 
 | ||||
| void jitbuf_clear(struct jitbuf_t *jb) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Push an entry into the buffer, with the given sequence number | ||||
|  * | ||||
|  * Pre: data is not NULL | ||||
|  */ | ||||
| 
 | ||||
| int jitbuf_push(struct jitbuf_t *jb, seq_t t, void *data) | ||||
| { | ||||
| 	void **entry; | ||||
| 
 | ||||
| 	assert(data != NULL); | ||||
| 
 | ||||
| 	if (jb->entries == 0 && t != jb->base) { | ||||
| 		fprintf(stderr, "Restarting (%d)\n", t); | ||||
| 		jb->base = t; | ||||
| 	} else { | ||||
| 		if (t < jb->base) { | ||||
| 			fprintf(stderr, "Late (%d)\n", t); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		if (t >= jb->base + JITBUF_MAX) { | ||||
| 			fprintf(stderr, "Early (%d)\n", t); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	entry = &jb->buf[(t - jb->base + jb->out) % JITBUF_MAX]; | ||||
| 
 | ||||
| 	if (*entry != NULL) { | ||||
| 		fprintf(stderr, "Dup (%d)\n", t); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	*entry = data; | ||||
| 	jb->entries++; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the entry at the front of the buffer, in sequence number order | ||||
|  * | ||||
|  * Return: NULL if no entry, otherwise entry next in sequence | ||||
|  */ | ||||
| 
 | ||||
| void* jitbuf_front(const struct jitbuf_t *jb) | ||||
| { | ||||
| 	return jb->buf[jb->out]; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Pop the next entry in the buffer | ||||
|  */ | ||||
| 
 | ||||
| void jitbuf_pop(struct jitbuf_t *jb) | ||||
| { | ||||
| 	void **entry; | ||||
| 
 | ||||
| 	entry = &jb->buf[jb->out]; | ||||
| 	if (*entry != NULL) { | ||||
| 		jb->entries--; | ||||
| 		*entry = NULL; | ||||
| 	} | ||||
| 	jb->base++; | ||||
| 	jb->out++; | ||||
| 	if (jb->out >= JITBUF_MAX) | ||||
| 		jb->out = 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Return true if this buffer has enough data flow to be declared ready | ||||
|  */ | ||||
| 
 | ||||
| bool jitbuf_ready(const struct jitbuf_t *jb, seq_t count) | ||||
| { | ||||
| 	size_t n; | ||||
| 
 | ||||
| 	assert(count <= JITBUF_MAX); | ||||
| 
 | ||||
| 	for (n = 0; n < count; n++) { | ||||
| 		if (jb->buf[(jb->out + n) % JITBUF_MAX] == NULL) | ||||
| 			return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| bool jitbuf_empty(const struct jitbuf_t *jb) | ||||
| { | ||||
| 	if (jb->entries > 0) | ||||
| 		return false; | ||||
| 	else | ||||
| 		return true; | ||||
| } | ||||
| 
 | ||||
| void jitbuf_debug(struct jitbuf_t *jb, FILE *fd) | ||||
| { | ||||
| 	seq_t n; | ||||
| 
 | ||||
| 	fprintf(fd, "% 3d ", jb->entries); | ||||
| 
 | ||||
| 	for (n = 0; n < JITBUF_MAX; n++) { | ||||
| 		if (jb->buf[(jb->out + n) % JITBUF_MAX] != NULL) { | ||||
| 			fputc('#', fd); | ||||
| 		} else { | ||||
| 			fputc(' ', fd); | ||||
| 		} | ||||
| 	} | ||||
| 	fputc('\n', fd); | ||||
| } | ||||
							
								
								
									
										34
									
								
								jitter.h
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								jitter.h
									
									
									
									
									
								
							| @ -1,34 +0,0 @@ | ||||
| #ifndef JITTER_H | ||||
| #define JITTER_H | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/types.h> | ||||
| 
 | ||||
| #define JITBUF_MAX 64 | ||||
| 
 | ||||
| typedef unsigned int seq_t; | ||||
| 
 | ||||
| /*
 | ||||
|  * De-jitter buffer | ||||
|  */ | ||||
| 
 | ||||
| struct jitbuf_t { | ||||
| 	seq_t base; | ||||
| 	size_t out, entries, contiguous; | ||||
| 	void *buf[JITBUF_MAX]; | ||||
| }; | ||||
| 
 | ||||
| void jitbuf_init(struct jitbuf_t *jb); | ||||
| void jitbuf_clear(struct jitbuf_t *jb); | ||||
| 
 | ||||
| int jitbuf_push(struct jitbuf_t *jb, seq_t t, void *data); | ||||
| void* jitbuf_front(const struct jitbuf_t *jb); | ||||
| void jitbuf_pop(struct jitbuf_t *jb); | ||||
| 
 | ||||
| bool jitbuf_ready(const struct jitbuf_t *jb, seq_t count); | ||||
| bool jitbuf_empty(const struct jitbuf_t *jb); | ||||
| 
 | ||||
| void jitbuf_debug(struct jitbuf_t *jb, FILE *fd); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										251
									
								
								rx.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								rx.c
									
									
									
									
									
								
							| @ -2,168 +2,106 @@ | ||||
| #include <string.h> | ||||
| #include <alsa/asoundlib.h> | ||||
| #include <celt/celt.h> | ||||
| #include <ortp/ortp.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/types.h> | ||||
| 
 | ||||
| #include "defaults.h" | ||||
| #include "device.h" | ||||
| #include "jitter.h" | ||||
| #include "sched.h" | ||||
| 
 | ||||
| #define ENCODED_BYTES 71 | ||||
| static unsigned int verbose = DEFAULT_VERBOSE; | ||||
| 
 | ||||
| /*
 | ||||
|  * Bind to a network socket for receiving packets on the given port | ||||
|  */ | ||||
| 
 | ||||
| static int listen_on_network(const char *service) | ||||
| static RtpSession* create_rtp_recv(const char *addr_desc, const int port, | ||||
| 		unsigned int jitter) | ||||
| { | ||||
| 	int r, sd; | ||||
| 	struct addrinfo hints, *servinfo, *p; | ||||
| 	RtpSession *session; | ||||
| 
 | ||||
| 	memset(&hints, 0, sizeof(hints)); | ||||
| 	hints.ai_family = AF_UNSPEC; | ||||
| 	hints.ai_socktype = SOCK_DGRAM; | ||||
| 	hints.ai_flags = AI_PASSIVE; /* use my IP */ | ||||
| 
 | ||||
| 	r = getaddrinfo(NULL, service, &hints, &servinfo); | ||||
| 	if (r != 0) { | ||||
| 		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r)); | ||||
| 		return -1; | ||||
| 	session = rtp_session_new(RTP_SESSION_RECVONLY); | ||||
| 	rtp_session_set_scheduling_mode(session, 0); | ||||
| 	rtp_session_set_blocking_mode(session, 0); | ||||
| 	rtp_session_set_local_addr(session, addr_desc, port); | ||||
| 	rtp_session_set_connected_mode(session, FALSE); | ||||
| 	rtp_session_enable_adaptive_jitter_compensation(session, TRUE); | ||||
| 	rtp_session_set_jitter_compensation(session, jitter); /* ms */ | ||||
| 	if (rtp_session_set_payload_type(session, 0) != 0) | ||||
| 		abort(); | ||||
| 	if (rtp_session_signal_connect(session, "timestamp_jump", | ||||
| 			(RtpCallback)rtp_session_resync, 0) != 0) | ||||
| 	{ | ||||
| 		abort(); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Bind to the first one we can */ | ||||
| 
 | ||||
| 	for (p = servinfo; p != NULL; p = p->ai_next) { | ||||
| 		sd = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, | ||||
| 				p->ai_protocol); | ||||
| 		if (sd == -1) { | ||||
| 			perror("socket"); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		if (bind(sd, p->ai_addr, p->ai_addrlen) == -1) { | ||||
| 			if (close(sd) == -1) | ||||
| 				abort(); | ||||
| 			perror("bind"); | ||||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	freeaddrinfo(servinfo); | ||||
| 
 | ||||
| 	if (p == NULL) { | ||||
| 		fputs("Failed to bind socket\n", stderr); | ||||
| 		return -1; | ||||
| 	} else { | ||||
| 		return sd; | ||||
| 	} | ||||
| 	return session; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Playback audio from the buffer to the audio device | ||||
|  */ | ||||
| 
 | ||||
| static int play_audio(struct jitbuf_t *jb, CELTDecoder *decoder, | ||||
| 		      snd_pcm_t *snd) | ||||
| static int play_one_frame(void *packet, | ||||
| 		size_t len, | ||||
| 		CELTDecoder *decoder, | ||||
| 		snd_pcm_t *snd, | ||||
| 		const unsigned int channels, | ||||
| 		const snd_pcm_uframes_t samples) | ||||
| { | ||||
| 	int r; | ||||
| 	void *data; | ||||
| 	float *pcm; | ||||
| 	snd_pcm_sframes_t f; | ||||
| 
 | ||||
| 	pcm = alloca(sizeof(float) * DEFAULT_FRAME * DEFAULT_CHANNELS); | ||||
| 	pcm = alloca(sizeof(float) * samples * channels); | ||||
| 
 | ||||
| 	data = jitbuf_front(jb); | ||||
| 	if (data == NULL) { | ||||
| 	if (packet == NULL) { | ||||
| 		r = celt_decode_float(decoder, NULL, 0, pcm); | ||||
| 	} else { | ||||
| 		r = celt_decode_float(decoder, data, ENCODED_BYTES, pcm); | ||||
| 		r = celt_decode_float(decoder, packet, len, pcm); | ||||
| 	} | ||||
| 	if (r != 0) { | ||||
| 		fputs("Error in celt_decode\n", stderr); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	f = snd_pcm_writei(snd, pcm, DEFAULT_FRAME); | ||||
| 	f = snd_pcm_writei(snd, pcm, samples); | ||||
| 	if (f < 0) { | ||||
| 		aerror("snd_pcm_writei", f); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (f < DEFAULT_FRAME) | ||||
| 	if (f < samples) | ||||
| 		fprintf(stderr, "Short write %ld\n", f); | ||||
| 
 | ||||
| 	jitbuf_pop(jb); | ||||
| 	free(data); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Read from the network into the buffer | ||||
|  */ | ||||
| 
 | ||||
| static int read_from_network(int sd, struct jitbuf_t *jb) | ||||
| static int run_rx(RtpSession *session, | ||||
| 		CELTDecoder *decoder, | ||||
| 		snd_pcm_t *snd, | ||||
| 		const unsigned int channels, | ||||
| 		const snd_pcm_uframes_t frame) | ||||
| { | ||||
| 	size_t z; | ||||
| 	char buf[32768]; | ||||
| 	void *data; | ||||
| 	seq_t seq; | ||||
| 
 | ||||
| 	z = recvfrom(sd, buf, sizeof(buf), 0, NULL, NULL); | ||||
| 	if (z == -1) { | ||||
| 		if (errno == EAGAIN) | ||||
| 			return -EAGAIN; | ||||
| 		perror("recvfrom"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (z != sizeof(uint32_t) + ENCODED_BYTES) { | ||||
| 		fputs("small packet", stderr); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/* First 32-bits is sequence number, rest is data */ | ||||
| 	seq = ntohl(*(uint32_t*)buf); | ||||
| 	z -= sizeof(uint32_t); | ||||
| 	assert(z >= 0); | ||||
| 	data = malloc(z); | ||||
| 	memcpy(data, buf + sizeof(uint32_t), z); | ||||
| 
 | ||||
| 	if (jitbuf_push(jb, seq, data) == -1) | ||||
| 		free(data); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * The main loop of receiving audio and playing it back | ||||
|  */ | ||||
| 
 | ||||
| static int run_rx(int sd, CELTDecoder *decoder, snd_pcm_t *snd) | ||||
| { | ||||
| 	int r; | ||||
| 	struct jitbuf_t jb; | ||||
| 
 | ||||
| 	jitbuf_init(&jb); | ||||
| 	int ts = 0; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		for (;;) { | ||||
| 			r = read_from_network(sd, &jb); | ||||
| 			if (r == -EAGAIN) | ||||
| 				break; | ||||
| 			if (r != 0) | ||||
| 				return -1; | ||||
| 		int r, have_more; | ||||
| 		char buf[32768]; | ||||
| 		void *packet; | ||||
| 
 | ||||
| 		r = rtp_session_recv_with_ts(session, buf, | ||||
| 				sizeof(buf), ts, &have_more); | ||||
| 		assert(r >= 0); | ||||
| 		assert(have_more == 0); | ||||
| 		if (r == 0) { | ||||
| 			packet = NULL; | ||||
| 			if (verbose > 1) | ||||
| 				fputc('#', stderr); | ||||
| 		} else { | ||||
| 			packet = buf; | ||||
| 			if (verbose > 1) | ||||
| 				fputc('.', stderr); | ||||
| 		} | ||||
| 
 | ||||
| 		//jitbuf_debug(&jb, stderr);
 | ||||
| 
 | ||||
| 		if (play_audio(&jb, decoder, snd) == -1) | ||||
| 		r = play_one_frame(packet, r, decoder, snd, channels, frame); | ||||
| 		if (r == -1) | ||||
| 			return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	jitbuf_clear(&jb); | ||||
| 		ts += frame; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void usage(FILE *fd) | ||||
| @ -171,32 +109,54 @@ static void usage(FILE *fd) | ||||
| 	fprintf(fd, "Usage: rx [<parameters>]\n"); | ||||
| 
 | ||||
| 	fprintf(fd, "\nAudio device (ALSA) parameters:\n"); | ||||
| 	fprintf(fd, "  -d <device>   Device name (default '%s')\n", | ||||
| 	fprintf(fd, "  -d <dev>    Device name (default '%s')\n", | ||||
| 		DEFAULT_DEVICE); | ||||
| 	fprintf(fd, "  -m <ms>       Buffer time (milliseconds, default %d)\n", | ||||
| 	fprintf(fd, "  -m <ms>     Buffer time (milliseconds, default %d)\n", | ||||
| 		DEFAULT_BUFFER); | ||||
| 
 | ||||
| 	fprintf(fd, "\nNetwork parameters:\n"); | ||||
| 	fprintf(fd, "  -p <port>     UDP port number or name (default %s)\n", | ||||
| 		DEFAULT_SERVICE); | ||||
| 	fprintf(fd, "  -h <addr>   IP address to listen on (default %s)\n", | ||||
| 		DEFAULT_ADDR); | ||||
| 	fprintf(fd, "  -p <port>   UDP port number (default %d)\n", | ||||
| 		DEFAULT_PORT); | ||||
| 	fprintf(fd, "  -j <ms>     Jitter buffer (milliseconds, default %d)\n", | ||||
| 		DEFAULT_JITTER); | ||||
| 
 | ||||
| 	fprintf(fd, "\nEncoding parameters (must match sender):\n"); | ||||
| 	fprintf(fd, "  -r <rate>   Sample rate (default %d)\n", | ||||
| 		DEFAULT_RATE); | ||||
| 	fprintf(fd, "  -c <n>      Number of channels (default %d)\n", | ||||
| 		DEFAULT_CHANNELS); | ||||
| 	fprintf(fd, "  -f <bytes>  Frame size (default %d)\n", | ||||
| 		DEFAULT_FRAME); | ||||
| 
 | ||||
| 	fprintf(fd, "\nDisplay parameters:\n"); | ||||
| 	fprintf(fd, "  -v <n>      Verbosity level (default %d)\n", | ||||
| 		DEFAULT_VERBOSE); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int sd, r; | ||||
| 	int r; | ||||
| 	snd_pcm_t *snd; | ||||
| 	CELTMode *mode; | ||||
| 	CELTDecoder *decoder; | ||||
| 	RtpSession *session; | ||||
| 
 | ||||
| 	/* command-line options */ | ||||
| 	const char *device = DEFAULT_DEVICE, | ||||
| 		*service = DEFAULT_SERVICE; | ||||
| 	unsigned int buffer = DEFAULT_BUFFER; | ||||
| 		*addr = DEFAULT_ADDR; | ||||
| 	unsigned int buffer = DEFAULT_BUFFER, | ||||
| 		rate = DEFAULT_RATE, | ||||
| 		frame = DEFAULT_FRAME, | ||||
| 		jitter = DEFAULT_JITTER, | ||||
| 		channels = DEFAULT_CHANNELS, | ||||
| 		port = DEFAULT_PORT; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		int c; | ||||
| 
 | ||||
| 		c = getopt(argc, argv, "d:m:p:"); | ||||
| 		c = getopt(argc, argv, "d:h:j:m:p:v:"); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
| 
 | ||||
| @ -204,11 +164,20 @@ int main(int argc, char *argv[]) | ||||
| 		case 'd': | ||||
| 			device = optarg; | ||||
| 			break; | ||||
| 		case 'h': | ||||
| 			addr = optarg; | ||||
| 			break; | ||||
| 		case 'j': | ||||
| 			jitter = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'm': | ||||
| 			buffer = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			service = optarg; | ||||
| 			port = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'v': | ||||
| 			verbose = atoi(optarg); | ||||
| 			break; | ||||
| 		default: | ||||
| 			usage(stderr); | ||||
| @ -216,8 +185,7 @@ int main(int argc, char *argv[]) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	mode = celt_mode_create(DEFAULT_RATE, DEFAULT_CHANNELS, DEFAULT_FRAME, | ||||
| 				NULL); | ||||
| 	mode = celt_mode_create(rate, channels, frame, NULL); | ||||
| 	if (mode == NULL) { | ||||
| 		fputs("celt_mode_create failed\n", stderr); | ||||
| 		return -1; | ||||
| @ -228,36 +196,35 @@ int main(int argc, char *argv[]) | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	sd = listen_on_network(service); | ||||
| 	if (sd == -1) | ||||
| 	if (go_realtime() != 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	ortp_init(); | ||||
| 	ortp_scheduler_init(); | ||||
| 	session = create_rtp_recv(addr, port, jitter); | ||||
| 	assert(session != NULL); | ||||
| 
 | ||||
| 	r = snd_pcm_open(&snd, device, SND_PCM_STREAM_PLAYBACK, 0); | ||||
| 	if (r < 0) { | ||||
| 		aerror("snd_pcm_open", r); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (set_alsa_hw(snd, DEFAULT_RATE, DEFAULT_CHANNELS, | ||||
| 			buffer * 1000) == -1) | ||||
| 	{ | ||||
| 	if (set_alsa_hw(snd, rate, channels, buffer * 1000) == -1) | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (set_alsa_sw(snd) == -1) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (go_realtime() != 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	r = run_rx(sd, decoder, snd); | ||||
| 	r = run_rx(session, decoder, snd, channels, frame); | ||||
| 
 | ||||
| 	if (snd_pcm_close(snd) < 0) | ||||
| 		abort(); | ||||
| 
 | ||||
| 	rtp_session_destroy(session); | ||||
| 	ortp_exit(); | ||||
| 	ortp_global_stats_display(); | ||||
| 
 | ||||
| 	celt_decoder_destroy(decoder); | ||||
| 	celt_mode_destroy(mode); | ||||
| 
 | ||||
| 	if (close(sd) == -1) | ||||
| 		abort(); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
| @ -1,34 +0,0 @@ | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include "jitter.h" | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int n; | ||||
| 	struct jitbuf_t jb; | ||||
| 	const char *test = "abcdefghijklmnopqrstuvwxyz1234567890"; | ||||
| 
 | ||||
| 	jitbuf_init(&jb); | ||||
| 
 | ||||
| 	for (n = 0; n < strlen(test); n++) { | ||||
| 		int m; | ||||
| 		char *out; | ||||
| 		size_t pos; | ||||
| 
 | ||||
| 		out = jitbuf_front(&jb); | ||||
| 		if (out != NULL) | ||||
| 			fputc(*out, stdout); | ||||
| 		else | ||||
| 			fputc('#', stdout); | ||||
| 		jitbuf_pop(&jb); | ||||
| 
 | ||||
| 		for (m = 0; m < 8; m++) { | ||||
| 			pos = n + rand() % (strlen(test) - n); | ||||
| 			jitbuf_push(&jb, pos, (void*)&test[pos]); | ||||
| 		} | ||||
| 	} | ||||
| 	fputc('\n', stdout); | ||||
| 
 | ||||
| 	jitbuf_clear(&jb); | ||||
| } | ||||
							
								
								
									
										172
									
								
								tx.c
									
									
									
									
									
								
							
							
						
						
									
										172
									
								
								tx.c
									
									
									
									
									
								
							| @ -2,6 +2,7 @@ | ||||
| #include <string.h> | ||||
| #include <alsa/asoundlib.h> | ||||
| #include <celt/celt.h> | ||||
| #include <ortp/ortp.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/types.h> | ||||
| 
 | ||||
| @ -9,162 +10,139 @@ | ||||
| #include "device.h" | ||||
| #include "sched.h" | ||||
| 
 | ||||
| typedef unsigned int seq_t; | ||||
| static unsigned int verbose = DEFAULT_VERBOSE; | ||||
| 
 | ||||
| /*
 | ||||
|  * Bind to network socket, for sending to the given host and port | ||||
|  */ | ||||
| 
 | ||||
| static int bind_to_network(const char *host, const char *service, | ||||
| 		struct addrinfo **servinfo, struct addrinfo **theirs) | ||||
| static RtpSession* create_rtp_send(const char *addr_desc, const int port) | ||||
| { | ||||
| 	int r, sd; | ||||
| 	struct addrinfo hints, *p; | ||||
| 	RtpSession *session; | ||||
| 
 | ||||
| 	memset(&hints, 0, sizeof(hints)); | ||||
| 	hints.ai_family = AF_UNSPEC; | ||||
| 	hints.ai_socktype = SOCK_DGRAM; | ||||
| 	session = rtp_session_new(RTP_SESSION_SENDONLY); | ||||
| 	assert(session != NULL); | ||||
| 
 | ||||
| 	r = getaddrinfo(host, service, &hints, servinfo); | ||||
| 	if (r != 0) { | ||||
| 		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r)); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	rtp_session_set_scheduling_mode(session, 0); | ||||
| 	rtp_session_set_blocking_mode(session, 0); | ||||
| 	rtp_session_set_connected_mode(session, FALSE); | ||||
| 	if (rtp_session_set_remote_addr(session, addr_desc, port) != 0) | ||||
| 		abort(); | ||||
| 	if (rtp_session_set_payload_type(session, 0) != 0) | ||||
| 		abort(); | ||||
| 	if (rtp_session_set_multicast_ttl(session, 16) != 0) | ||||
| 		abort(); | ||||
| 
 | ||||
| 	for (p = *servinfo; p != NULL; p = p->ai_next) { | ||||
| 		sd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); | ||||
| 		if (sd == -1) { | ||||
| 			perror("socket"); | ||||
| 			continue; | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	if (p == NULL) { | ||||
| 		fputs("Failed to bind socket\n", stderr); | ||||
| 		return -1; | ||||
| 	} else { | ||||
| 		*theirs = p; | ||||
| 		return sd; | ||||
| 	} | ||||
| 	return session; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Encode a frame of audio and send it over the network | ||||
|  */ | ||||
| 
 | ||||
| static int send_audio(snd_pcm_t *snd, | ||||
| 		const snd_pcm_uframes_t samples, | ||||
| static int send_one_frame(snd_pcm_t *snd, | ||||
| 		const unsigned int channels, | ||||
| 		const snd_pcm_uframes_t samples, | ||||
| 		CELTEncoder *encoder, | ||||
| 		const size_t bytes_per_frame, | ||||
| 		int sd, struct addrinfo *theirs, seq_t *seq) | ||||
| 		RtpSession *session) | ||||
| { | ||||
| 	float *pcm; | ||||
| 	void *packet, *encoded; | ||||
| 	void *packet; | ||||
| 	ssize_t z; | ||||
| 	snd_pcm_sframes_t f; | ||||
| 	static int ts = 0; | ||||
| 
 | ||||
| 	pcm = alloca(sizeof(float) * samples * channels); | ||||
| 	packet = alloca(bytes_per_frame + sizeof(uint32_t)); | ||||
| 	encoded = packet + sizeof(uint32_t); | ||||
| 	packet = alloca(bytes_per_frame); | ||||
| 
 | ||||
| 	f = snd_pcm_readi(snd, pcm, samples); | ||||
| 	if (f < 0) { | ||||
| 		aerror("snd_pcm_readi", f); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	if (f < DEFAULT_FRAME) | ||||
| 	if (f < samples) | ||||
| 		fprintf(stderr, "Short read, %ld\n", f); | ||||
| 
 | ||||
| 	z = celt_encode_float(encoder, pcm, NULL, encoded, bytes_per_frame); | ||||
| 	z = celt_encode_float(encoder, pcm, NULL, packet, bytes_per_frame); | ||||
| 	if (z < 0) { | ||||
| 		fputs("celt_encode_float failed\n", stderr); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	*(uint32_t*)packet = htonl(*seq); | ||||
| 	(*seq)++; | ||||
| 
 | ||||
| 	z = sendto(sd, packet, sizeof(uint32_t) + bytes_per_frame, 0, | ||||
| 			theirs->ai_addr, theirs->ai_addrlen); | ||||
| 	if (z == -1) { | ||||
| 		perror("sendto"); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	//fputc('>', stderr);
 | ||||
| 	rtp_session_send_with_ts(session, packet, z, ts); | ||||
| 	ts += samples; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int run_tx(snd_pcm_t *snd, | ||||
| 		const snd_pcm_uframes_t samples, | ||||
| 		const unsigned int channels, | ||||
| 		const snd_pcm_uframes_t frame, | ||||
| 		CELTEncoder *encoder, | ||||
| 		const size_t bytes_per_frame, | ||||
| 		int sd, struct addrinfo *theirs) | ||||
| 		RtpSession *session) | ||||
| { | ||||
| 	seq_t seq = 0; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		int r; | ||||
| 
 | ||||
| 		r = send_audio(snd, samples, channels, | ||||
| 		r = send_one_frame(snd, channels, frame, | ||||
| 				encoder, bytes_per_frame, | ||||
| 				sd, theirs, &seq); | ||||
| 				session); | ||||
| 		if (r == -1) | ||||
| 			return -1; | ||||
| 
 | ||||
| 		if (verbose > 1) | ||||
| 			fputc('>', stderr); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void usage(FILE *fd) | ||||
| { | ||||
| 	fprintf(fd, "Usage: tx [<parameters>] <host>\n"); | ||||
| 	fprintf(fd, "Usage: tx [<parameters>]\n"); | ||||
| 
 | ||||
| 	fprintf(fd, "\nAudio device (ALSA) parameters:\n"); | ||||
| 	fprintf(fd, "  -d <device>   Device name (default '%s')\n", | ||||
| 	fprintf(fd, "  -d <dev>    Device name (default '%s')\n", | ||||
| 		DEFAULT_DEVICE); | ||||
| 	fprintf(fd, "  -m <ms>       Buffer time (milliseconds, default %d)\n", | ||||
| 	fprintf(fd, "  -m <ms>     Buffer time (milliseconds, default %d)\n", | ||||
| 		DEFAULT_BUFFER); | ||||
| 
 | ||||
| 	fprintf(fd, "\nNetwork parameters:\n"); | ||||
| 	fprintf(fd, "  -p <port>     UDP port number or name (default %s)\n", | ||||
| 		DEFAULT_SERVICE); | ||||
| 	fprintf(fd, "  -f <bytes>    Frame size (default %d)\n", | ||||
| 		DEFAULT_FRAME); | ||||
| 	fprintf(fd, "  -h <addr>   IP address to send to (default %s)\n", | ||||
| 		DEFAULT_ADDR); | ||||
| 	fprintf(fd, "  -p <port>   UDP port number (default %d)\n", | ||||
| 		DEFAULT_PORT); | ||||
| 
 | ||||
| 	fprintf(fd, "\nEncoding parameters:\n"); | ||||
| 	fprintf(fd, "  -r <rate>     Sample rate (default %d)\n", | ||||
| 	fprintf(fd, "  -r <rate>   Sample rate (default %d)\n", | ||||
| 		DEFAULT_RATE); | ||||
| 	fprintf(fd, "  -c <n>        Number of channels (default %d)\n", | ||||
| 	fprintf(fd, "  -c <n>      Number of channels (default %d)\n", | ||||
| 		DEFAULT_CHANNELS); | ||||
| 	fprintf(fd, "  -b <bitrate>  Bitrate (kbps, approx., default %d)\n", | ||||
| 	fprintf(fd, "  -f <bytes>  Frame size (default %d)\n", | ||||
| 		DEFAULT_FRAME); | ||||
| 	fprintf(fd, "  -b <kbps>   Bitrate (approx., default %d)\n", | ||||
| 		DEFAULT_BITRATE); | ||||
| 
 | ||||
| 	fprintf(fd, "\nDisplay parameters:\n"); | ||||
| 	fprintf(fd, "  -v <n>      Verbosity level (default %d)\n", | ||||
| 		DEFAULT_VERBOSE); | ||||
| } | ||||
| 
 | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int sd, r; | ||||
| 	struct addrinfo *servinfo, *theirs; | ||||
| 	int r; | ||||
| 	size_t bytes_per_frame; | ||||
| 	snd_pcm_t *snd; | ||||
| 	CELTMode *mode; | ||||
| 	CELTEncoder *encoder; | ||||
| 	size_t bytes_per_frame; | ||||
| 	RtpSession *session; | ||||
| 
 | ||||
| 	/* command-line options */ | ||||
| 	const char *device = DEFAULT_DEVICE, | ||||
| 		*service = DEFAULT_SERVICE, | ||||
| 		*host; | ||||
| 		*addr = DEFAULT_ADDR; | ||||
| 	unsigned int buffer = DEFAULT_BUFFER, | ||||
| 		rate = DEFAULT_RATE, | ||||
| 		channels = DEFAULT_CHANNELS, | ||||
| 		frame = DEFAULT_FRAME, | ||||
| 		kbps = DEFAULT_BITRATE; | ||||
| 		kbps = DEFAULT_BITRATE, | ||||
| 		port = DEFAULT_PORT; | ||||
| 
 | ||||
| 	for (;;) { | ||||
| 		int c; | ||||
| 
 | ||||
| 		c = getopt(argc, argv, "b:c:d:f:m:p:r:"); | ||||
| 		c = getopt(argc, argv, "b:c:d:f:h:m:p:r:v:"); | ||||
| 		if (c == -1) | ||||
| 			break; | ||||
| 
 | ||||
| @ -181,28 +159,27 @@ int main(int argc, char *argv[]) | ||||
| 		case 'f': | ||||
| 			frame = atol(optarg); | ||||
| 			break; | ||||
| 		case 'h': | ||||
| 			addr = optarg; | ||||
| 			break; | ||||
| 		case 'm': | ||||
| 			buffer = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			service = optarg; | ||||
| 			port = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'r': | ||||
| 			rate = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'v': | ||||
| 			verbose = atoi(optarg); | ||||
| 			break; | ||||
| 		default: | ||||
| 			usage(stderr); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (argv[optind] == NULL) { | ||||
| 		usage(stderr); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	host = argv[optind]; | ||||
| 
 | ||||
| 	mode = celt_mode_create(rate, channels, frame, NULL); | ||||
| 	if (mode == NULL) { | ||||
| 		fputs("celt_mode_create failed\n", stderr); | ||||
| @ -216,10 +193,15 @@ int main(int argc, char *argv[]) | ||||
| 	bytes_per_frame = kbps * 1024 * frame / rate / 8; | ||||
| 	fprintf(stderr, "bytes_per_frame = %d\n", bytes_per_frame); | ||||
| 
 | ||||
| 	sd = bind_to_network(host, service, &servinfo, &theirs); | ||||
| 	if (sd == -1) | ||||
| 	if (go_realtime() != 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	ortp_init(); | ||||
| 	ortp_scheduler_init(); | ||||
| 	ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR); | ||||
| 	session = create_rtp_send(addr, port); | ||||
| 	assert(session != NULL); | ||||
| 
 | ||||
| 	r = snd_pcm_open(&snd, device, SND_PCM_STREAM_CAPTURE, 0); | ||||
| 	if (r < 0) { | ||||
| 		aerror("snd_pcm_open", r); | ||||
| @ -230,21 +212,17 @@ int main(int argc, char *argv[]) | ||||
| 	if (set_alsa_sw(snd) == -1) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (go_realtime() != 0) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	r = run_tx(snd, frame, channels, encoder, bytes_per_frame, sd, theirs); | ||||
| 
 | ||||
| 	freeaddrinfo(servinfo); | ||||
| 	r = run_tx(snd, channels, frame, encoder, bytes_per_frame, session); | ||||
| 
 | ||||
| 	if (snd_pcm_close(snd) < 0) | ||||
| 		abort(); | ||||
| 
 | ||||
| 	rtp_session_destroy(session); | ||||
| 	ortp_exit(); | ||||
| 	ortp_global_stats_display(); | ||||
| 
 | ||||
| 	celt_encoder_destroy(encoder); | ||||
| 	celt_mode_destroy(mode); | ||||
| 
 | ||||
| 	if (close(sd) == -1) | ||||
| 		abort(); | ||||
| 
 | ||||
| 	return r; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Mark Hills
						Mark Hills