trx based on libortp

This commit is contained in:
Mark Hills 2009-09-11 16:40:27 +01:00
parent c7af697019
commit d2be2e03eb
7 changed files with 198 additions and 446 deletions

View File

@ -2,23 +2,24 @@
CFLAGS+=-MMD -Wall CFLAGS+=-MMD -Wall
LDLIBS_ASOUND?=-lasound
LDLIBS_CELT?=-lcelt
LDLIBS_ORTP?=-lortp
LDLIBS+=$(LDLIBS_ASOUND) $(LDLIBS_CELT) $(LDLIBS_ORTP)
.PHONY: all clean .PHONY: all clean
all: rx tx all: rx tx
rx: rx.o device.o jitter.o sched.o rx: rx.o device.o sched.o
rx: LDLIBS+=-lcelt -lasound
tx: tx.o device.o sched.o tx: tx.o device.o sched.o
tx: LDLIBS+=-lcelt -lasound
test-jitter: test-jitter.o jitter.o
clean: clean:
rm -f *.o rm -f *.o
rm -f *.d rm -f *.d
rm -f tx rm -f tx
rm -f rx rm -f rx
rm -f test-jitter
-include *.d -include *.d

View File

@ -4,11 +4,15 @@
#define DEFAULT_DEVICE "default" #define DEFAULT_DEVICE "default"
#define DEFAULT_BUFFER 16 #define DEFAULT_BUFFER 16
#define DEFAULT_SERVICE "1350" #define DEFAULT_ADDR "0.0.0.0"
#define DEFAULT_FRAME 128 #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_CHANNELS 2
#define DEFAULT_BITRATE 128 #define DEFAULT_BITRATE 128
#define DEFAULT_VERBOSE 1
#endif #endif

130
jitter.c
View File

@ -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);
}

View File

@ -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
View File

@ -2,168 +2,106 @@
#include <string.h> #include <string.h>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <celt/celt.h> #include <celt/celt.h>
#include <ortp/ortp.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include "defaults.h" #include "defaults.h"
#include "device.h" #include "device.h"
#include "jitter.h"
#include "sched.h" #include "sched.h"
#define ENCODED_BYTES 71 static unsigned int verbose = DEFAULT_VERBOSE;
/* static RtpSession* create_rtp_recv(const char *addr_desc, const int port,
* Bind to a network socket for receiving packets on the given port unsigned int jitter)
*/
static int listen_on_network(const char *service)
{ {
int r, sd; RtpSession *session;
struct addrinfo hints, *servinfo, *p;
memset(&hints, 0, sizeof(hints)); session = rtp_session_new(RTP_SESSION_RECVONLY);
hints.ai_family = AF_UNSPEC; rtp_session_set_scheduling_mode(session, 0);
hints.ai_socktype = SOCK_DGRAM; rtp_session_set_blocking_mode(session, 0);
hints.ai_flags = AI_PASSIVE; /* use my IP */ rtp_session_set_local_addr(session, addr_desc, port);
rtp_session_set_connected_mode(session, FALSE);
r = getaddrinfo(NULL, service, &hints, &servinfo); rtp_session_enable_adaptive_jitter_compensation(session, TRUE);
if (r != 0) { rtp_session_set_jitter_compensation(session, jitter); /* ms */
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r)); if (rtp_session_set_payload_type(session, 0) != 0)
return -1; abort();
if (rtp_session_signal_connect(session, "timestamp_jump",
(RtpCallback)rtp_session_resync, 0) != 0)
{
abort();
} }
/* Bind to the first one we can */ return session;
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;
}
} }
/* static int play_one_frame(void *packet,
* Playback audio from the buffer to the audio device size_t len,
*/ CELTDecoder *decoder,
snd_pcm_t *snd,
static int play_audio(struct jitbuf_t *jb, CELTDecoder *decoder, const unsigned int channels,
snd_pcm_t *snd) const snd_pcm_uframes_t samples)
{ {
int r; int r;
void *data;
float *pcm; float *pcm;
snd_pcm_sframes_t f; snd_pcm_sframes_t f;
pcm = alloca(sizeof(float) * DEFAULT_FRAME * DEFAULT_CHANNELS); pcm = alloca(sizeof(float) * samples * channels);
data = jitbuf_front(jb); if (packet == NULL) {
if (data == NULL) {
r = celt_decode_float(decoder, NULL, 0, pcm); r = celt_decode_float(decoder, NULL, 0, pcm);
} else { } else {
r = celt_decode_float(decoder, data, ENCODED_BYTES, pcm); r = celt_decode_float(decoder, packet, len, pcm);
} }
if (r != 0) { if (r != 0) {
fputs("Error in celt_decode\n", stderr); fputs("Error in celt_decode\n", stderr);
return -1; return -1;
} }
f = snd_pcm_writei(snd, pcm, DEFAULT_FRAME); f = snd_pcm_writei(snd, pcm, samples);
if (f < 0) { if (f < 0) {
aerror("snd_pcm_writei", f); aerror("snd_pcm_writei", f);
return -1; return -1;
} }
if (f < DEFAULT_FRAME) if (f < samples)
fprintf(stderr, "Short write %ld\n", f); fprintf(stderr, "Short write %ld\n", f);
jitbuf_pop(jb);
free(data);
return 0; return 0;
} }
/* static int run_rx(RtpSession *session,
* Read from the network into the buffer CELTDecoder *decoder,
*/ snd_pcm_t *snd,
const unsigned int channels,
static int read_from_network(int sd, struct jitbuf_t *jb) const snd_pcm_uframes_t frame)
{ {
size_t z; int ts = 0;
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);
for (;;) { for (;;) {
for (;;) { int r, have_more;
r = read_from_network(sd, &jb); char buf[32768];
if (r == -EAGAIN) void *packet;
break;
if (r != 0) r = rtp_session_recv_with_ts(session, buf,
return -1; 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); r = play_one_frame(packet, r, decoder, snd, channels, frame);
if (r == -1)
if (play_audio(&jb, decoder, snd) == -1)
return -1; return -1;
}
jitbuf_clear(&jb); ts += frame;
}
} }
static void usage(FILE *fd) static void usage(FILE *fd)
@ -171,32 +109,54 @@ static void usage(FILE *fd)
fprintf(fd, "Usage: rx [<parameters>]\n"); fprintf(fd, "Usage: rx [<parameters>]\n");
fprintf(fd, "\nAudio device (ALSA) 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); DEFAULT_DEVICE);
fprintf(fd, " -m <ms> Buffer time (milliseconds, default %d)\n", fprintf(fd, " -m <ms> Buffer time (milliseconds, default %d)\n",
DEFAULT_BUFFER); DEFAULT_BUFFER);
fprintf(fd, "\nNetwork parameters:\n"); fprintf(fd, "\nNetwork parameters:\n");
fprintf(fd, " -p <port> UDP port number or name (default %s)\n", fprintf(fd, " -h <addr> IP address to listen on (default %s)\n",
DEFAULT_SERVICE); 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 main(int argc, char *argv[])
{ {
int sd, r; int r;
snd_pcm_t *snd; snd_pcm_t *snd;
CELTMode *mode; CELTMode *mode;
CELTDecoder *decoder; CELTDecoder *decoder;
RtpSession *session;
/* command-line options */ /* command-line options */
const char *device = DEFAULT_DEVICE, const char *device = DEFAULT_DEVICE,
*service = DEFAULT_SERVICE; *addr = DEFAULT_ADDR;
unsigned int buffer = DEFAULT_BUFFER; unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE,
frame = DEFAULT_FRAME,
jitter = DEFAULT_JITTER,
channels = DEFAULT_CHANNELS,
port = DEFAULT_PORT;
for (;;) { for (;;) {
int c; int c;
c = getopt(argc, argv, "d:m:p:"); c = getopt(argc, argv, "d:h:j:m:p:v:");
if (c == -1) if (c == -1)
break; break;
@ -204,11 +164,20 @@ int main(int argc, char *argv[])
case 'd': case 'd':
device = optarg; device = optarg;
break; break;
case 'h':
addr = optarg;
break;
case 'j':
jitter = atoi(optarg);
break;
case 'm': case 'm':
buffer = atoi(optarg); buffer = atoi(optarg);
break; break;
case 'p': case 'p':
service = optarg; port = atoi(optarg);
break;
case 'v':
verbose = atoi(optarg);
break; break;
default: default:
usage(stderr); usage(stderr);
@ -216,8 +185,7 @@ int main(int argc, char *argv[])
} }
} }
mode = celt_mode_create(DEFAULT_RATE, DEFAULT_CHANNELS, DEFAULT_FRAME, mode = celt_mode_create(rate, channels, frame, NULL);
NULL);
if (mode == NULL) { if (mode == NULL) {
fputs("celt_mode_create failed\n", stderr); fputs("celt_mode_create failed\n", stderr);
return -1; return -1;
@ -228,36 +196,35 @@ int main(int argc, char *argv[])
return -1; return -1;
} }
sd = listen_on_network(service); if (go_realtime() != 0)
if (sd == -1)
return -1; 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); r = snd_pcm_open(&snd, device, SND_PCM_STREAM_PLAYBACK, 0);
if (r < 0) { if (r < 0) {
aerror("snd_pcm_open", r); aerror("snd_pcm_open", r);
return -1; return -1;
} }
if (set_alsa_hw(snd, DEFAULT_RATE, DEFAULT_CHANNELS, if (set_alsa_hw(snd, rate, channels, buffer * 1000) == -1)
buffer * 1000) == -1)
{
return -1; return -1;
}
if (set_alsa_sw(snd) == -1) if (set_alsa_sw(snd) == -1)
return -1; return -1;
if (go_realtime() != 0) r = run_rx(session, decoder, snd, channels, frame);
return -1;
r = run_rx(sd, decoder, snd);
if (snd_pcm_close(snd) < 0) if (snd_pcm_close(snd) < 0)
abort(); abort();
rtp_session_destroy(session);
ortp_exit();
ortp_global_stats_display();
celt_decoder_destroy(decoder); celt_decoder_destroy(decoder);
celt_mode_destroy(mode); celt_mode_destroy(mode);
if (close(sd) == -1)
abort();
return r; return r;
} }

View File

@ -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
View File

@ -2,6 +2,7 @@
#include <string.h> #include <string.h>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <celt/celt.h> #include <celt/celt.h>
#include <ortp/ortp.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
@ -9,162 +10,139 @@
#include "device.h" #include "device.h"
#include "sched.h" #include "sched.h"
typedef unsigned int seq_t; static unsigned int verbose = DEFAULT_VERBOSE;
/* static RtpSession* create_rtp_send(const char *addr_desc, const int port)
* 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)
{ {
int r, sd; RtpSession *session;
struct addrinfo hints, *p;
memset(&hints, 0, sizeof(hints)); session = rtp_session_new(RTP_SESSION_SENDONLY);
hints.ai_family = AF_UNSPEC; assert(session != NULL);
hints.ai_socktype = SOCK_DGRAM;
r = getaddrinfo(host, service, &hints, servinfo); rtp_session_set_scheduling_mode(session, 0);
if (r != 0) { rtp_session_set_blocking_mode(session, 0);
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r)); rtp_session_set_connected_mode(session, FALSE);
return -1; 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) { return session;
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;
}
} }
/* static int send_one_frame(snd_pcm_t *snd,
* 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,
const unsigned int channels, const unsigned int channels,
const snd_pcm_uframes_t samples,
CELTEncoder *encoder, CELTEncoder *encoder,
const size_t bytes_per_frame, const size_t bytes_per_frame,
int sd, struct addrinfo *theirs, seq_t *seq) RtpSession *session)
{ {
float *pcm; float *pcm;
void *packet, *encoded; void *packet;
ssize_t z; ssize_t z;
snd_pcm_sframes_t f; snd_pcm_sframes_t f;
static int ts = 0;
pcm = alloca(sizeof(float) * samples * channels); pcm = alloca(sizeof(float) * samples * channels);
packet = alloca(bytes_per_frame + sizeof(uint32_t)); packet = alloca(bytes_per_frame);
encoded = packet + sizeof(uint32_t);
f = snd_pcm_readi(snd, pcm, samples); f = snd_pcm_readi(snd, pcm, samples);
if (f < 0) { if (f < 0) {
aerror("snd_pcm_readi", f); aerror("snd_pcm_readi", f);
return -1; return -1;
} }
if (f < DEFAULT_FRAME) if (f < samples)
fprintf(stderr, "Short read, %ld\n", f); 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) { if (z < 0) {
fputs("celt_encode_float failed\n", stderr); fputs("celt_encode_float failed\n", stderr);
return -1; return -1;
} }
*(uint32_t*)packet = htonl(*seq); rtp_session_send_with_ts(session, packet, z, ts);
(*seq)++; ts += samples;
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);
return 0; return 0;
} }
static int run_tx(snd_pcm_t *snd, static int run_tx(snd_pcm_t *snd,
const snd_pcm_uframes_t samples,
const unsigned int channels, const unsigned int channels,
const snd_pcm_uframes_t frame,
CELTEncoder *encoder, CELTEncoder *encoder,
const size_t bytes_per_frame, const size_t bytes_per_frame,
int sd, struct addrinfo *theirs) RtpSession *session)
{ {
seq_t seq = 0;
for (;;) { for (;;) {
int r; int r;
r = send_audio(snd, samples, channels, r = send_one_frame(snd, channels, frame,
encoder, bytes_per_frame, encoder, bytes_per_frame,
sd, theirs, &seq); session);
if (r == -1) if (r == -1)
return -1; return -1;
if (verbose > 1)
fputc('>', stderr);
} }
} }
static void usage(FILE *fd) 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, "\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); DEFAULT_DEVICE);
fprintf(fd, " -m <ms> Buffer time (milliseconds, default %d)\n", fprintf(fd, " -m <ms> Buffer time (milliseconds, default %d)\n",
DEFAULT_BUFFER); DEFAULT_BUFFER);
fprintf(fd, "\nNetwork parameters:\n"); fprintf(fd, "\nNetwork parameters:\n");
fprintf(fd, " -p <port> UDP port number or name (default %s)\n", fprintf(fd, " -h <addr> IP address to send to (default %s)\n",
DEFAULT_SERVICE); DEFAULT_ADDR);
fprintf(fd, " -f <bytes> Frame size (default %d)\n", fprintf(fd, " -p <port> UDP port number (default %d)\n",
DEFAULT_FRAME); DEFAULT_PORT);
fprintf(fd, "\nEncoding parameters:\n"); 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); DEFAULT_RATE);
fprintf(fd, " -c <n> Number of channels (default %d)\n", fprintf(fd, " -c <n> Number of channels (default %d)\n",
DEFAULT_CHANNELS); 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); 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 main(int argc, char *argv[])
{ {
int sd, r; int r;
struct addrinfo *servinfo, *theirs; size_t bytes_per_frame;
snd_pcm_t *snd; snd_pcm_t *snd;
CELTMode *mode; CELTMode *mode;
CELTEncoder *encoder; CELTEncoder *encoder;
size_t bytes_per_frame; RtpSession *session;
/* command-line options */ /* command-line options */
const char *device = DEFAULT_DEVICE, const char *device = DEFAULT_DEVICE,
*service = DEFAULT_SERVICE, *addr = DEFAULT_ADDR;
*host;
unsigned int buffer = DEFAULT_BUFFER, unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE, rate = DEFAULT_RATE,
channels = DEFAULT_CHANNELS, channels = DEFAULT_CHANNELS,
frame = DEFAULT_FRAME, frame = DEFAULT_FRAME,
kbps = DEFAULT_BITRATE; kbps = DEFAULT_BITRATE,
port = DEFAULT_PORT;
for (;;) { for (;;) {
int c; 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) if (c == -1)
break; break;
@ -181,28 +159,27 @@ int main(int argc, char *argv[])
case 'f': case 'f':
frame = atol(optarg); frame = atol(optarg);
break; break;
case 'h':
addr = optarg;
break;
case 'm': case 'm':
buffer = atoi(optarg); buffer = atoi(optarg);
break; break;
case 'p': case 'p':
service = optarg; port = atoi(optarg);
break; break;
case 'r': case 'r':
rate = atoi(optarg); rate = atoi(optarg);
break; break;
case 'v':
verbose = atoi(optarg);
break;
default: default:
usage(stderr); usage(stderr);
return -1; return -1;
} }
} }
if (argv[optind] == NULL) {
usage(stderr);
return -1;
}
host = argv[optind];
mode = celt_mode_create(rate, channels, frame, NULL); mode = celt_mode_create(rate, channels, frame, NULL);
if (mode == NULL) { if (mode == NULL) {
fputs("celt_mode_create failed\n", stderr); 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; bytes_per_frame = kbps * 1024 * frame / rate / 8;
fprintf(stderr, "bytes_per_frame = %d\n", bytes_per_frame); fprintf(stderr, "bytes_per_frame = %d\n", bytes_per_frame);
sd = bind_to_network(host, service, &servinfo, &theirs); if (go_realtime() != 0)
if (sd == -1)
return -1; 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); r = snd_pcm_open(&snd, device, SND_PCM_STREAM_CAPTURE, 0);
if (r < 0) { if (r < 0) {
aerror("snd_pcm_open", r); aerror("snd_pcm_open", r);
@ -230,21 +212,17 @@ int main(int argc, char *argv[])
if (set_alsa_sw(snd) == -1) if (set_alsa_sw(snd) == -1)
return -1; return -1;
if (go_realtime() != 0) r = run_tx(snd, channels, frame, encoder, bytes_per_frame, session);
return -1;
r = run_tx(snd, frame, channels, encoder, bytes_per_frame, sd, theirs);
freeaddrinfo(servinfo);
if (snd_pcm_close(snd) < 0) if (snd_pcm_close(snd) < 0)
abort(); abort();
rtp_session_destroy(session);
ortp_exit();
ortp_global_stats_display();
celt_encoder_destroy(encoder); celt_encoder_destroy(encoder);
celt_mode_destroy(mode); celt_mode_destroy(mode);
if (close(sd) == -1)
abort();
return r; return r;
} }