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
(C) Copyright 2020 Mark Hills <mark@xwax.org>
(C) Copyright 2012 Mark Hills <mark@xwax.org>
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
* 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_S16);
r = snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_FLOAT);
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 = -1;
r = snd_pcm_hw_params_set_buffer_time_near(pcm, hw, &buffer, &dir);
CHK("snd_pcm_hw_params_set_buffer_time_near", 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);
r = snd_pcm_hw_params(pcm, hw);
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
* 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 2020 Mark Hills <mark@xwax.org>"
#define COPYRIGHT "trx (C) Copyright 2012 Mark Hills <mark@xwax.org>"
#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
* modify it under the terms of the GNU General Public License
@ -32,7 +32,7 @@
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)
fputc('|', stderr);
@ -45,13 +45,13 @@ static RtpSession* create_rtp_recv(const char *addr_desc, const int port,
RtpSession *session;
session = rtp_session_new(RTP_SESSION_RECVONLY);
rtp_session_set_scheduling_mode(session, FALSE);
rtp_session_set_blocking_mode(session, FALSE);
rtp_session_set_scheduling_mode(session, 0);
rtp_session_set_blocking_mode(session, 0);
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 */
rtp_session_set_time_jump_limit(session, jitter * 2); /* ms */
if (rtp_session_set_payload_type(session, 0) != 0)
abort();
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();
}
/*
* 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;
}
@ -79,15 +70,15 @@ static int play_one_frame(void *packet,
const unsigned int channels)
{
int r;
int16_t *pcm;
float *pcm;
snd_pcm_sframes_t f, samples = 1920;
pcm = alloca(sizeof(*pcm) * samples * channels);
pcm = alloca(sizeof(float) * samples * channels);
if (packet == NULL) {
r = opus_decode(decoder, NULL, 0, pcm, samples, 1);
r = opus_decode_float(decoder, NULL, 0, pcm, samples, 1);
} else {
r = opus_decode(decoder, packet, len, pcm, samples, 0);
r = opus_decode_float(decoder, packet, len, pcm, samples, 0);
}
if (r < 0) {
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);
if (f < 0) {
f = snd_pcm_recover(snd, f, 0);
if (f < 0) {
aerror("snd_pcm_writei", f);
return -1;
}
return 0;
aerror("snd_pcm_writei", f);
return -1;
}
if (f < r)
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",
DEFAULT_CHANNELS);
fprintf(fd, "\nProgram parameters:\n");
fprintf(fd, "\nDisplay 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[])
@ -186,8 +172,7 @@ int main(int argc, char *argv[])
/* command-line options */
const char *device = DEFAULT_DEVICE,
*addr = DEFAULT_ADDR,
*pid = NULL;
*addr = DEFAULT_ADDR;
unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE,
jitter = DEFAULT_JITTER,
@ -227,9 +212,6 @@ int main(int argc, char *argv[])
case 'v':
verbose = atoi(optarg);
break;
case 'D':
pid = optarg;
break;
default:
usage(stderr);
return -1;
@ -258,9 +240,6 @@ 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);

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
* 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 <stdio.h>
#include <unistd.h>
#include "sched.h"
@ -30,48 +27,23 @@
int go_realtime(void)
{
int max_pri;
const struct sched_param sp = {
.sched_priority = REALTIME_PRIORITY,
};
struct sched_param sp;
if (sched_getparam(0, &sp)) {
perror("sched_getparam");
return -1;
}
max_pri = sched_get_priority_max(SCHED_FIFO);
sp.sched_priority = REALTIME_PRIORITY;
if (sp.sched_priority > max_pri) {
fprintf(stderr, "Invalid priority (maximum %d)\n", max_pri);
return -1;
}
errno = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
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");
if (sched_setscheduler(0, SCHED_FIFO, &sp)) {
perror("sched_setscheduler");
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
* modify it under the terms of the GNU General Public License
@ -21,6 +21,5 @@
#define MISC_H
int go_realtime(void);
int go_daemon(const char *pid_file);
#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
* 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();
if (rtp_session_set_multicast_ttl(session, 16) != 0)
abort();
if (rtp_session_set_dscp(session, 40) != 0)
abort();
return session;
}
@ -62,38 +60,24 @@ static int send_one_frame(snd_pcm_t *snd,
const unsigned int ts_per_frame,
RtpSession *session)
{
int16_t *pcm;
float *pcm;
void *packet;
ssize_t z;
snd_pcm_sframes_t f;
static unsigned int ts = 0;
pcm = alloca(sizeof(*pcm) * samples * channels);
pcm = alloca(sizeof(float) * samples * channels);
packet = alloca(bytes_per_frame);
f = snd_pcm_readi(snd, pcm, samples);
if (f < 0) {
if (f == -ESTRPIPE)
ts = 0;
f = snd_pcm_recover(snd, f, 0);
if (f < 0) {
aerror("snd_pcm_readi", f);
return -1;
}
return 0;
aerror("snd_pcm_readi", f);
return -1;
}
/* 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) {
if (f < samples)
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) {
fprintf(stderr, "opus_encode_float: %s\n", opus_strerror(z));
return -1;
@ -154,10 +138,9 @@ static void usage(FILE *fd)
fprintf(fd, " -b <kbps> Bitrate (approx., default %d)\n",
DEFAULT_BITRATE);
fprintf(fd, "\nProgram parameters:\n");
fprintf(fd, "\nDisplay 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");
@ -174,8 +157,7 @@ int main(int argc, char *argv[])
/* command-line options */
const char *device = DEFAULT_DEVICE,
*addr = DEFAULT_ADDR,
*pid = NULL;
*addr = DEFAULT_ADDR;
unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE,
channels = DEFAULT_CHANNELS,
@ -188,7 +170,7 @@ int main(int argc, char *argv[])
for (;;) {
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)
break;
@ -220,9 +202,6 @@ int main(int argc, char *argv[])
case 'v':
verbose = atoi(optarg);
break;
case 'D':
pid = optarg;
break;
default:
usage(stderr);
return -1;
@ -245,6 +224,7 @@ int main(int argc, char *argv[])
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);
@ -258,9 +238,6 @@ 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);