mirror of http://www.pogo.org.uk/~mark/trx.git
Compare commits
14 Commits
Author | SHA1 | Date |
---|---|---|
Mark Hills | 5c844e6996 | |
Mark Hills | 69b6537303 | |
Mark Hills | 85fe843d61 | |
Mark Hills | 66b4707a24 | |
Mark Hills | e375ceef5a | |
Mark Hills | 0b53299941 | |
Mark Hills | f35eb4efe2 | |
Mark Hills | 14927fbaa9 | |
Mark Hills | e1682f4dd6 | |
Mark Hills | b53aa73137 | |
Mark Hills | 374308b810 | |
Mark Hills | 591a498725 | |
Mark Hills | b7ac96036d | |
Mark Hills | ad80d6b1c1 |
2
README
2
README
|
@ -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.
|
||||
|
||||
|
|
10
device.c
10
device.c
|
@ -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);
|
||||
|
|
2
device.h
2
device.h
|
@ -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
|
||||
|
|
2
notice.h
2
notice.h
|
@ -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
|
||||
|
|
47
rx.c
47
rx.c
|
@ -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,7 +32,7 @@
|
|||
|
||||
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)
|
||||
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, 0);
|
||||
rtp_session_set_blocking_mode(session, 0);
|
||||
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 * 2); /* 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",
|
||||
|
@ -60,6 +60,15 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -70,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));
|
||||
|
@ -87,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);
|
||||
|
@ -158,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[])
|
||||
|
@ -172,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,
|
||||
|
@ -212,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;
|
||||
|
@ -240,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
31
sched.c
|
@ -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;
|
||||
}
|
||||
|
|
3
sched.h
3
sched.h
|
@ -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
|
||||
|
|
49
tx.c
49
tx.c
|
@ -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;
|
||||
|
@ -224,7 +245,6 @@ 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);
|
||||
|
||||
|
@ -238,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);
|
||||
|
|
Loading…
Reference in New Issue