1
0
mirror of http://www.pogo.org.uk/~mark/trx.git synced 2024-09-28 22:45:06 +02:00

Compare commits

..

No commits in common. "master" and "0.2" have entirely different histories.
master ... 0.2

8 changed files with 43 additions and 116 deletions

2
README
View File

@ -1,6 +1,6 @@
trx: Realtime audio over IP trx: Realtime audio over IP
(C) Copyright 2020 Mark Hills <mark@xwax.org> (C) Copyright 2012 Mark Hills <mark@xwax.org>
See the COPYING file for licensing terms. See the COPYING file for licensing terms.

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2020 Mark Hills <mark@xwax.org> * Copyright (C) 2012 Mark Hills <mark@xwax.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -53,7 +53,7 @@ int set_alsa_hw(snd_pcm_t *pcm,
r = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED); r = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
CHK("snd_pcm_hw_params_set_access", r); CHK("snd_pcm_hw_params_set_access", r);
r = snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16); r = snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_FLOAT);
CHK("snd_pcm_hw_params_set_format", r); CHK("snd_pcm_hw_params_set_format", r);
r = snd_pcm_hw_params_set_rate(pcm, hw, rate, 0); r = snd_pcm_hw_params_set_rate(pcm, hw, rate, 0);
@ -62,9 +62,9 @@ int set_alsa_hw(snd_pcm_t *pcm,
r = snd_pcm_hw_params_set_channels(pcm, hw, channels); r = snd_pcm_hw_params_set_channels(pcm, hw, channels);
CHK("snd_pcm_hw_params_set_channels", r); CHK("snd_pcm_hw_params_set_channels", r);
dir = -1; dir = 0;
r = snd_pcm_hw_params_set_buffer_time_near(pcm, hw, &buffer, &dir); r = snd_pcm_hw_params_set_buffer_time_max(pcm, hw, &buffer, &dir);
CHK("snd_pcm_hw_params_set_buffer_time_near", r); CHK("snd_pcm_hw_params_set_buffer_time_max", r);
r = snd_pcm_hw_params(pcm, hw); r = snd_pcm_hw_params(pcm, hw);
CHK("hw_params", r); CHK("hw_params", r);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2020 Mark Hills <mark@xwax.org> * Copyright (C) 2012 Mark Hills <mark@xwax.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License

View File

@ -1,6 +1,6 @@
#ifndef NOTICE_H #ifndef NOTICE_H
#define NOTICE_H #define NOTICE_H
#define COPYRIGHT "trx (C) Copyright 2020 Mark Hills <mark@xwax.org>" #define COPYRIGHT "trx (C) Copyright 2012 Mark Hills <mark@xwax.org>"
#endif #endif

47
rx.c
View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2020 Mark Hills <mark@xwax.org> * Copyright (C) 2012 Mark Hills <mark@xwax.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -32,7 +32,7 @@
static unsigned int verbose = DEFAULT_VERBOSE; static unsigned int verbose = DEFAULT_VERBOSE;
static void timestamp_jump(RtpSession *session, void *a, void *b, void *c) static void timestamp_jump(RtpSession *session, ...)
{ {
if (verbose > 1) if (verbose > 1)
fputc('|', stderr); fputc('|', stderr);
@ -45,13 +45,13 @@ static RtpSession* create_rtp_recv(const char *addr_desc, const int port,
RtpSession *session; RtpSession *session;
session = rtp_session_new(RTP_SESSION_RECVONLY); session = rtp_session_new(RTP_SESSION_RECVONLY);
rtp_session_set_scheduling_mode(session, FALSE); rtp_session_set_scheduling_mode(session, 0);
rtp_session_set_blocking_mode(session, FALSE); rtp_session_set_blocking_mode(session, 0);
rtp_session_set_local_addr(session, addr_desc, port, -1); rtp_session_set_local_addr(session, addr_desc, port, -1);
rtp_session_set_connected_mode(session, FALSE); rtp_session_set_connected_mode(session, FALSE);
rtp_session_enable_adaptive_jitter_compensation(session, TRUE); rtp_session_enable_adaptive_jitter_compensation(session, TRUE);
rtp_session_set_jitter_compensation(session, jitter); /* ms */ rtp_session_set_jitter_compensation(session, jitter); /* ms */
rtp_session_set_time_jump_limit(session, jitter * 16); /* ms */ rtp_session_set_time_jump_limit(session, jitter * 2); /* ms */
if (rtp_session_set_payload_type(session, 0) != 0) if (rtp_session_set_payload_type(session, 0) != 0)
abort(); abort();
if (rtp_session_signal_connect(session, "timestamp_jump", if (rtp_session_signal_connect(session, "timestamp_jump",
@ -60,15 +60,6 @@ static RtpSession* create_rtp_recv(const char *addr_desc, const int port,
abort(); abort();
} }
/*
* oRTP in RECVONLY mode attempts to send RTCP packets and
* segfaults (v4.3.0 tested)
*
* https://stackoverflow.com/questions/43591690/receiving-rtcp-issues-within-ortp-library
*/
rtp_session_enable_rtcp(session, FALSE);
return session; return session;
} }
@ -79,15 +70,15 @@ static int play_one_frame(void *packet,
const unsigned int channels) const unsigned int channels)
{ {
int r; int r;
int16_t *pcm; float *pcm;
snd_pcm_sframes_t f, samples = 1920; snd_pcm_sframes_t f, samples = 1920;
pcm = alloca(sizeof(*pcm) * samples * channels); pcm = alloca(sizeof(float) * samples * channels);
if (packet == NULL) { if (packet == NULL) {
r = opus_decode(decoder, NULL, 0, pcm, samples, 1); r = opus_decode_float(decoder, NULL, 0, pcm, samples, 1);
} else { } else {
r = opus_decode(decoder, packet, len, pcm, samples, 0); r = opus_decode_float(decoder, packet, len, pcm, samples, 0);
} }
if (r < 0) { if (r < 0) {
fprintf(stderr, "opus_decode: %s\n", opus_strerror(r)); fprintf(stderr, "opus_decode: %s\n", opus_strerror(r));
@ -96,12 +87,8 @@ static int play_one_frame(void *packet,
f = snd_pcm_writei(snd, pcm, r); f = snd_pcm_writei(snd, pcm, r);
if (f < 0) { if (f < 0) {
f = snd_pcm_recover(snd, f, 0); aerror("snd_pcm_writei", f);
if (f < 0) { return -1;
aerror("snd_pcm_writei", f);
return -1;
}
return 0;
} }
if (f < r) if (f < r)
fprintf(stderr, "Short write %ld\n", f); fprintf(stderr, "Short write %ld\n", f);
@ -171,10 +158,9 @@ static void usage(FILE *fd)
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, "\nProgram parameters:\n"); fprintf(fd, "\nDisplay parameters:\n");
fprintf(fd, " -v <n> Verbosity level (default %d)\n", fprintf(fd, " -v <n> Verbosity level (default %d)\n",
DEFAULT_VERBOSE); DEFAULT_VERBOSE);
fprintf(fd, " -D <file> Run as a daemon, writing process ID to the given file\n");
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -186,8 +172,7 @@ int main(int argc, char *argv[])
/* command-line options */ /* command-line options */
const char *device = DEFAULT_DEVICE, const char *device = DEFAULT_DEVICE,
*addr = DEFAULT_ADDR, *addr = DEFAULT_ADDR;
*pid = NULL;
unsigned int buffer = DEFAULT_BUFFER, unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE, rate = DEFAULT_RATE,
jitter = DEFAULT_JITTER, jitter = DEFAULT_JITTER,
@ -227,9 +212,6 @@ int main(int argc, char *argv[])
case 'v': case 'v':
verbose = atoi(optarg); verbose = atoi(optarg);
break; break;
case 'D':
pid = optarg;
break;
default: default:
usage(stderr); usage(stderr);
return -1; return -1;
@ -258,9 +240,6 @@ int main(int argc, char *argv[])
if (set_alsa_sw(snd) == -1) if (set_alsa_sw(snd) == -1)
return -1; return -1;
if (pid)
go_daemon(pid);
go_realtime(); go_realtime();
r = run_rx(session, decoder, snd, channels, rate); r = run_rx(session, decoder, snd, channels, rate);

48
sched.c
View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2020 Mark Hills <mark@xwax.org> * Copyright (C) 2012 Mark Hills <mark@xwax.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -17,11 +17,8 @@
* *
*/ */
#include <errno.h>
#include <pthread.h>
#include <sched.h> #include <sched.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include "sched.h" #include "sched.h"
@ -30,48 +27,23 @@
int go_realtime(void) int go_realtime(void)
{ {
int max_pri; int max_pri;
const struct sched_param sp = { struct sched_param sp;
.sched_priority = REALTIME_PRIORITY,
}; if (sched_getparam(0, &sp)) {
perror("sched_getparam");
return -1;
}
max_pri = sched_get_priority_max(SCHED_FIFO); max_pri = sched_get_priority_max(SCHED_FIFO);
sp.sched_priority = REALTIME_PRIORITY;
if (sp.sched_priority > max_pri) { if (sp.sched_priority > max_pri) {
fprintf(stderr, "Invalid priority (maximum %d)\n", max_pri); fprintf(stderr, "Invalid priority (maximum %d)\n", max_pri);
return -1; return -1;
} }
errno = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp); if (sched_setscheduler(0, SCHED_FIFO, &sp)) {
if (errno) { perror("sched_setscheduler");
perror("pthread_setschedparam");
return -1;
}
return 0;
}
int go_daemon(const char *pid_file)
{
FILE *f;
if (daemon(0, 0) == -1) {
perror("daemon");
return -1;
}
if (!pid_file)
return 0;
f = fopen(pid_file, "w");
if (!f) {
perror("fopen");
return -1;
}
fprintf(f, "%d", getpid());
if (fclose(f) != 0) {
perror("fclose");
return -1; return -1;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2020 Mark Hills <mark@xwax.org> * Copyright (C) 2012 Mark Hills <mark@xwax.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -21,6 +21,5 @@
#define MISC_H #define MISC_H
int go_realtime(void); int go_realtime(void);
int go_daemon(const char *pid_file);
#endif #endif

45
tx.c
View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2020 Mark Hills <mark@xwax.org> * Copyright (C) 2012 Mark Hills <mark@xwax.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -48,8 +48,6 @@ static RtpSession* create_rtp_send(const char *addr_desc, const int port)
abort(); abort();
if (rtp_session_set_multicast_ttl(session, 16) != 0) if (rtp_session_set_multicast_ttl(session, 16) != 0)
abort(); abort();
if (rtp_session_set_dscp(session, 40) != 0)
abort();
return session; return session;
} }
@ -62,38 +60,24 @@ static int send_one_frame(snd_pcm_t *snd,
const unsigned int ts_per_frame, const unsigned int ts_per_frame,
RtpSession *session) RtpSession *session)
{ {
int16_t *pcm; float *pcm;
void *packet; void *packet;
ssize_t z; ssize_t z;
snd_pcm_sframes_t f; snd_pcm_sframes_t f;
static unsigned int ts = 0; static unsigned int ts = 0;
pcm = alloca(sizeof(*pcm) * samples * channels); pcm = alloca(sizeof(float) * samples * channels);
packet = alloca(bytes_per_frame); packet = alloca(bytes_per_frame);
f = snd_pcm_readi(snd, pcm, samples); f = snd_pcm_readi(snd, pcm, samples);
if (f < 0) { if (f < 0) {
if (f == -ESTRPIPE) aerror("snd_pcm_readi", f);
ts = 0; return -1;
f = snd_pcm_recover(snd, f, 0);
if (f < 0) {
aerror("snd_pcm_readi", f);
return -1;
}
return 0;
} }
if (f < samples)
/* Opus encoder requires a complete frame, so if we xrun
* mid-frame then we discard the incomplete audio. The next
* read will catch the error condition and recover */
if (f < samples) {
fprintf(stderr, "Short read, %ld\n", f); fprintf(stderr, "Short read, %ld\n", f);
return 0;
}
z = opus_encode(encoder, pcm, samples, packet, bytes_per_frame); z = opus_encode_float(encoder, pcm, samples, packet, bytes_per_frame);
if (z < 0) { if (z < 0) {
fprintf(stderr, "opus_encode_float: %s\n", opus_strerror(z)); fprintf(stderr, "opus_encode_float: %s\n", opus_strerror(z));
return -1; return -1;
@ -154,10 +138,9 @@ static void usage(FILE *fd)
fprintf(fd, " -b <kbps> Bitrate (approx., default %d)\n", fprintf(fd, " -b <kbps> Bitrate (approx., default %d)\n",
DEFAULT_BITRATE); DEFAULT_BITRATE);
fprintf(fd, "\nProgram parameters:\n"); fprintf(fd, "\nDisplay parameters:\n");
fprintf(fd, " -v <n> Verbosity level (default %d)\n", fprintf(fd, " -v <n> Verbosity level (default %d)\n",
DEFAULT_VERBOSE); DEFAULT_VERBOSE);
fprintf(fd, " -D <file> Run as a daemon, writing process ID to the given file\n");
fprintf(fd, "\nAllowed frame sizes (-f) are defined by the Opus codec. For example,\n" fprintf(fd, "\nAllowed frame sizes (-f) are defined by the Opus codec. For example,\n"
"at 48000Hz the permitted values are 120, 240, 480 or 960.\n"); "at 48000Hz the permitted values are 120, 240, 480 or 960.\n");
@ -174,8 +157,7 @@ int main(int argc, char *argv[])
/* command-line options */ /* command-line options */
const char *device = DEFAULT_DEVICE, const char *device = DEFAULT_DEVICE,
*addr = DEFAULT_ADDR, *addr = DEFAULT_ADDR;
*pid = NULL;
unsigned int buffer = DEFAULT_BUFFER, unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE, rate = DEFAULT_RATE,
channels = DEFAULT_CHANNELS, channels = DEFAULT_CHANNELS,
@ -188,7 +170,7 @@ int main(int argc, char *argv[])
for (;;) { for (;;) {
int c; int c;
c = getopt(argc, argv, "b:c:d:f:h:m:p:r:v:D:"); c = getopt(argc, argv, "b:c:d:f:h:m:p:r:v:");
if (c == -1) if (c == -1)
break; break;
@ -220,9 +202,6 @@ int main(int argc, char *argv[])
case 'v': case 'v':
verbose = atoi(optarg); verbose = atoi(optarg);
break; break;
case 'D':
pid = optarg;
break;
default: default:
usage(stderr); usage(stderr);
return -1; return -1;
@ -245,6 +224,7 @@ int main(int argc, char *argv[])
ortp_init(); ortp_init();
ortp_scheduler_init(); ortp_scheduler_init();
ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR);
session = create_rtp_send(addr, port); session = create_rtp_send(addr, port);
assert(session != NULL); assert(session != NULL);
@ -258,9 +238,6 @@ int main(int argc, char *argv[])
if (set_alsa_sw(snd) == -1) if (set_alsa_sw(snd) == -1)
return -1; return -1;
if (pid)
go_daemon(pid);
go_realtime(); go_realtime();
r = run_tx(snd, channels, frame, encoder, bytes_per_frame, r = run_tx(snd, channels, frame, encoder, bytes_per_frame,
ts_per_frame, session); ts_per_frame, session);