Compare commits

...

18 Commits
0.1 ... master

Author SHA1 Message Date
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
Mark Hills f84c7e0726 Port to the widespread versions of ortp library 2014-03-14 14:48:12 +00:00
Mark Hills 1d4c64dd9f Use 8kHz reference rate
We're using payload 0, for which the reference rate for timestamps is
8kHz. We could use the proper payload type for Opus codec but this is
not available in the installed libortp, and we are not aiming for
compatibility with other applications.
2014-03-10 22:25:41 +00:00
Mark Hills 55d398c46c Enfore a time jump relative to the jitter buffer
Without this, use of the -j flag can easily exceed the default time
jump.
2014-03-10 20:01:18 +00:00
Mark Hills a7904a4e08 Notify when the timestamp resync occurs 2014-03-10 20:01:00 +00:00
8 changed files with 122 additions and 42 deletions

2
README
View File

@ -1,6 +1,6 @@
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.

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
* 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);
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);
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);
CHK("snd_pcm_hw_params_set_channels", r);
dir = 0;
r = snd_pcm_hw_params_set_buffer_time_max(pcm, hw, &buffer, &dir);
CHK("snd_pcm_hw_params_set_buffer_time_max", r);
dir = -1;
r = snd_pcm_hw_params_set_buffer_time_near(pcm, hw, &buffer, &dir);
CHK("snd_pcm_hw_params_set_buffer_time_near", r);
r = snd_pcm_hw_params(pcm, hw);
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
* modify it under the terms of the GNU General Public License

View File

@ -1,6 +1,6 @@
#ifndef 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

60
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
* modify it under the terms of the GNU General Public License
@ -32,26 +32,43 @@
static unsigned int verbose = DEFAULT_VERBOSE;
static void timestamp_jump(RtpSession *session, void *a, void *b, void *c)
{
if (verbose > 1)
fputc('|', stderr);
rtp_session_resync(session);
}
static RtpSession* create_rtp_recv(const char *addr_desc, const int port,
unsigned int jitter)
{
RtpSession *session;
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_scheduling_mode(session, FALSE);
rtp_session_set_blocking_mode(session, FALSE);
rtp_session_set_local_addr(session, addr_desc, port, -1);
rtp_session_set_connected_mode(session, FALSE);
rtp_session_enable_adaptive_jitter_compensation(session, TRUE);
rtp_session_set_jitter_compensation(session, jitter); /* ms */
rtp_session_set_time_jump_limit(session, jitter * 16); /* 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)
timestamp_jump, 0) != 0)
{
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;
}
@ -62,15 +79,15 @@ static int play_one_frame(void *packet,
const unsigned int channels)
{
int r;
float *pcm;
int16_t *pcm;
snd_pcm_sframes_t f, samples = 1920;
pcm = alloca(sizeof(float) * samples * channels);
pcm = alloca(sizeof(*pcm) * samples * channels);
if (packet == NULL) {
r = opus_decode_float(decoder, NULL, 0, pcm, samples, 1);
r = opus_decode(decoder, NULL, 0, pcm, samples, 1);
} else {
r = opus_decode_float(decoder, packet, len, pcm, samples, 0);
r = opus_decode(decoder, packet, len, pcm, samples, 0);
}
if (r < 0) {
fprintf(stderr, "opus_decode: %s\n", opus_strerror(r));
@ -79,8 +96,12 @@ static int play_one_frame(void *packet,
f = snd_pcm_writei(snd, pcm, r);
if (f < 0) {
aerror("snd_pcm_writei", f);
return -1;
f = snd_pcm_recover(snd, f, 0);
if (f < 0) {
aerror("snd_pcm_writei", f);
return -1;
}
return 0;
}
if (f < r)
fprintf(stderr, "Short write %ld\n", f);
@ -119,10 +140,9 @@ static int run_rx(RtpSession *session,
if (r == -1)
return -1;
/* Follow the RFC, 48kHz is reference rate for
* timestamps */
/* Follow the RFC, payload 0 has 8kHz reference rate */
ts += r * 48000 / rate;
ts += r * 8000 / rate;
}
}
@ -151,9 +171,10 @@ static void usage(FILE *fd)
fprintf(fd, " -c <n> Number of channels (default %d)\n",
DEFAULT_CHANNELS);
fprintf(fd, "\nDisplay parameters:\n");
fprintf(fd, "\nProgram parameters:\n");
fprintf(fd, " -v <n> Verbosity level (default %d)\n",
DEFAULT_VERBOSE);
fprintf(fd, " -D <file> Run as a daemon, writing process ID to the given file\n");
}
int main(int argc, char *argv[])
@ -165,7 +186,8 @@ int main(int argc, char *argv[])
/* command-line options */
const char *device = DEFAULT_DEVICE,
*addr = DEFAULT_ADDR;
*addr = DEFAULT_ADDR,
*pid = NULL;
unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE,
jitter = DEFAULT_JITTER,
@ -205,6 +227,9 @@ int main(int argc, char *argv[])
case 'v':
verbose = atoi(optarg);
break;
case 'D':
pid = optarg;
break;
default:
usage(stderr);
return -1;
@ -233,6 +258,9 @@ int main(int argc, char *argv[])
if (set_alsa_sw(snd) == -1)
return -1;
if (pid)
go_daemon(pid);
go_realtime();
r = run_rx(session, decoder, snd, channels, rate);

31
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
* modify it under the terms of the GNU General Public License
@ -19,6 +19,7 @@
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include "sched.h"
@ -49,3 +50,31 @@ int go_realtime(void)
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 0;
}

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
* modify it under the terms of the GNU General Public License
@ -21,5 +21,6 @@
#define MISC_H
int go_realtime(void);
int go_daemon(const char *pid_file);
#endif

54
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
* 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();
if (rtp_session_set_multicast_ttl(session, 16) != 0)
abort();
if (rtp_session_set_dscp(session, 40) != 0)
abort();
return session;
}
@ -60,24 +62,38 @@ static int send_one_frame(snd_pcm_t *snd,
const unsigned int ts_per_frame,
RtpSession *session)
{
float *pcm;
int16_t *pcm;
void *packet;
ssize_t z;
snd_pcm_sframes_t f;
static unsigned int ts = 0;
pcm = alloca(sizeof(float) * samples * channels);
pcm = alloca(sizeof(*pcm) * samples * channels);
packet = alloca(bytes_per_frame);
f = snd_pcm_readi(snd, pcm, samples);
if (f < 0) {
aerror("snd_pcm_readi", f);
return -1;
}
if (f < samples)
fprintf(stderr, "Short read, %ld\n", f);
if (f == -ESTRPIPE)
ts = 0;
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) {
fprintf(stderr, "opus_encode_float: %s\n", opus_strerror(z));
return -1;
@ -138,9 +154,10 @@ static void usage(FILE *fd)
fprintf(fd, " -b <kbps> Bitrate (approx., default %d)\n",
DEFAULT_BITRATE);
fprintf(fd, "\nDisplay parameters:\n");
fprintf(fd, "\nProgram parameters:\n");
fprintf(fd, " -v <n> Verbosity level (default %d)\n",
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"
"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 */
const char *device = DEFAULT_DEVICE,
*addr = DEFAULT_ADDR;
*addr = DEFAULT_ADDR,
*pid = NULL;
unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE,
channels = DEFAULT_CHANNELS,
@ -170,7 +188,7 @@ int main(int argc, char *argv[])
for (;;) {
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)
break;
@ -202,6 +220,9 @@ int main(int argc, char *argv[])
case 'v':
verbose = atoi(optarg);
break;
case 'D':
pid = optarg;
break;
default:
usage(stderr);
return -1;
@ -218,14 +239,12 @@ int main(int argc, char *argv[])
bytes_per_frame = kbps * 1024 * frame / rate / 8;
/* Follow the RFC, 48kHz is reference rate for
* timestamps */
/* Follow the RFC, payload 0 has 8kHz reference rate */
ts_per_frame = frame * 48000 / rate;
ts_per_frame = frame * 8000 / rate;
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);
@ -239,6 +258,9 @@ int main(int argc, char *argv[])
if (set_alsa_sw(snd) == -1)
return -1;
if (pid)
go_daemon(pid);
go_realtime();
r = run_tx(snd, channels, frame, encoder, bytes_per_frame,
ts_per_frame, session);