1
0
mirror of http://www.pogo.org.uk/~mark/trx.git synced 2025-07-11 08:32:05 +02:00

Compare commits

...

15 Commits
0.2 ... master

Author SHA1 Message Date
Mark Hills
0c5ab0dfc1 Do not use the ambiguous sched_setscheduler()
POSIX specifies this is per-process, and GNU implemented it on the
thread. Musl realises the problem with this and so doesn't implement
it at all, forcing this more specific alternative to be used.
2023-05-12 16:31:03 +01:00
Mark Hills
5c844e6996 Use the default logging level
This is a #define to bctoolbox which means we have to link ourselves
to that library. Whereas oRTP defines its own link. It turns out the
default logging level is appropriate, so use that.

This also removes one of the compatibility problems caused by
different versions of oRTP which had different definitions of this
function.
2020-11-28 11:03:34 +00:00
Mark Hills
69b6537303 The year is now 2020 2020-01-04 11:31:45 +00:00
Mark Hills
85fe843d61 oRTP segfaults in RECVONLY mode
Reported-by: Paul Le Couteur <paullecouteur@gmail.com>
2020-01-04 11:30:29 +00:00
Mark Hills
66b4707a24 Use DSCP to help with wifi links 2018-01-23 16:21:51 +00:00
Mark Hills
e375ceef5a Use fixed point 2018-01-23 16:20:21 +00:00
Mark Hills
0b53299941 Implicitly look for a new stream on resume 2018-01-23 16:19:46 +00:00
Mark Hills
f35eb4efe2 Change to 0.26.0 liboRTP API 2018-01-23 16:18:46 +00:00
Mark Hills
14927fbaa9 Update copyright 2014-10-22 21:22:10 +01:00
Mark Hills
e1682f4dd6 Add an optional daemon mode 2014-10-22 21:21:14 +01:00
Mark Hills
b53aa73137 Increase time jump 2014-03-24 05:41:04 -04:00
Mark Hills
374308b810 Non-fatal handling of xruns 2014-03-24 05:40:28 -04:00
Mark Hills
591a498725 Be more specific when setting buffer time
We don't want the max buffer time, but we want the nearest buffer
time that is below the value provided by the user.
2014-03-21 18:43:52 +00:00
Mark Hills
b7ac96036d Use constants for clarity 2014-03-21 18:43:52 +00:00
Mark Hills
ad80d6b1c1 Be less verbose 2014-03-21 18:43:51 +00:00
8 changed files with 118 additions and 45 deletions

2
README
View File

@ -1,6 +1,6 @@
trx: Realtime audio over IP trx: Realtime audio over IP
(C) Copyright 2012 Mark Hills <mark@xwax.org> (C) Copyright 2020 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) 2012 Mark Hills <mark@xwax.org> * Copyright (C) 2020 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_FLOAT); r = snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16);
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 = 0; dir = -1;
r = snd_pcm_hw_params_set_buffer_time_max(pcm, hw, &buffer, &dir); r = snd_pcm_hw_params_set_buffer_time_near(pcm, hw, &buffer, &dir);
CHK("snd_pcm_hw_params_set_buffer_time_max", r); CHK("snd_pcm_hw_params_set_buffer_time_near", 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) 2012 Mark Hills <mark@xwax.org> * Copyright (C) 2020 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 2012 Mark Hills <mark@xwax.org>" #define COPYRIGHT "trx (C) Copyright 2020 Mark Hills <mark@xwax.org>"
#endif #endif

47
rx.c
View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012 Mark Hills <mark@xwax.org> * Copyright (C) 2020 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, ...) static void timestamp_jump(RtpSession *session, void *a, void *b, void *c)
{ {
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, 0); rtp_session_set_scheduling_mode(session, FALSE);
rtp_session_set_blocking_mode(session, 0); rtp_session_set_blocking_mode(session, FALSE);
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 * 2); /* ms */ rtp_session_set_time_jump_limit(session, jitter * 16); /* 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,6 +60,15 @@ 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;
} }
@ -70,15 +79,15 @@ static int play_one_frame(void *packet,
const unsigned int channels) const unsigned int channels)
{ {
int r; int r;
float *pcm; int16_t *pcm;
snd_pcm_sframes_t f, samples = 1920; snd_pcm_sframes_t f, samples = 1920;
pcm = alloca(sizeof(float) * samples * channels); pcm = alloca(sizeof(*pcm) * samples * channels);
if (packet == NULL) { if (packet == NULL) {
r = opus_decode_float(decoder, NULL, 0, pcm, samples, 1); r = opus_decode(decoder, NULL, 0, pcm, samples, 1);
} else { } else {
r = opus_decode_float(decoder, packet, len, pcm, samples, 0); r = opus_decode(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));
@ -87,8 +96,12 @@ 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) {
aerror("snd_pcm_writei", f); f = snd_pcm_recover(snd, f, 0);
return -1; if (f < 0) {
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);
@ -158,9 +171,10 @@ 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, "\nDisplay parameters:\n"); fprintf(fd, "\nProgram 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[])
@ -172,7 +186,8 @@ 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,
@ -212,6 +227,9 @@ 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;
@ -240,6 +258,9 @@ 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) 2012 Mark Hills <mark@xwax.org> * Copyright (C) 2020 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,8 +17,11 @@
* *
*/ */
#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"
@ -27,23 +30,48 @@
int go_realtime(void) int go_realtime(void)
{ {
int max_pri; int max_pri;
struct sched_param sp; const 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;
} }
if (sched_setscheduler(0, SCHED_FIFO, &sp)) { errno = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
perror("sched_setscheduler"); if (errno) {
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) 2012 Mark Hills <mark@xwax.org> * Copyright (C) 2020 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,5 +21,6 @@
#define MISC_H #define MISC_H
int go_realtime(void); int go_realtime(void);
int go_daemon(const char *pid_file);
#endif #endif

49
tx.c
View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2012 Mark Hills <mark@xwax.org> * Copyright (C) 2020 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,6 +48,8 @@ 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;
} }
@ -60,24 +62,38 @@ 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)
{ {
float *pcm; int16_t *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(float) * samples * channels); pcm = alloca(sizeof(*pcm) * 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) {
aerror("snd_pcm_readi", f); if (f == -ESTRPIPE)
return -1; ts = 0;
}
if (f < samples)
fprintf(stderr, "Short read, %ld\n", f);
z = opus_encode_float(encoder, pcm, samples, packet, bytes_per_frame); f = snd_pcm_recover(snd, f, 0);
if (f < 0) {
aerror("snd_pcm_readi", f);
return -1;
}
return 0;
}
/* 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);
return 0;
}
z = opus_encode(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;
@ -138,9 +154,10 @@ 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, "\nDisplay parameters:\n"); fprintf(fd, "\nProgram 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");
@ -157,7 +174,8 @@ 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,
@ -170,7 +188,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:"); c = getopt(argc, argv, "b:c:d:f:h:m:p:r:v:D:");
if (c == -1) if (c == -1)
break; break;
@ -202,6 +220,9 @@ 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;
@ -224,7 +245,6 @@ 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);
@ -238,6 +258,9 @@ 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);