trx/rx.c

278 lines
6.2 KiB
C
Raw Normal View History

2012-07-27 13:50:03 +02:00
/*
* 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
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
2009-09-05 00:04:44 +02:00
#include <netdb.h>
#include <string.h>
#include <alsa/asoundlib.h>
2012-07-21 17:14:13 +02:00
#include <opus/opus.h>
2009-09-11 17:40:27 +02:00
#include <ortp/ortp.h>
2009-09-05 00:04:44 +02:00
#include <sys/socket.h>
#include <sys/types.h>
#include "defaults.h"
#include "device.h"
2012-07-21 14:32:40 +02:00
#include "notice.h"
2009-09-05 00:04:44 +02:00
#include "sched.h"
2009-09-11 17:40:27 +02:00
static unsigned int verbose = DEFAULT_VERBOSE;
2009-09-05 00:04:44 +02:00
2018-01-23 17:18:46 +01:00
static void timestamp_jump(RtpSession *session, void *a, void *b, void *c)
{
if (verbose > 1)
fputc('|', stderr);
rtp_session_resync(session);
}
2009-09-11 17:40:27 +02:00
static RtpSession* create_rtp_recv(const char *addr_desc, const int port,
unsigned int jitter)
2009-09-05 00:04:44 +02:00
{
2009-09-11 17:40:27 +02:00
RtpSession *session;
session = rtp_session_new(RTP_SESSION_RECVONLY);
2014-03-21 18:34:10 +01:00
rtp_session_set_scheduling_mode(session, FALSE);
rtp_session_set_blocking_mode(session, FALSE);
rtp_session_set_local_addr(session, addr_desc, port, -1);
2009-09-11 17:40:27 +02:00
rtp_session_set_connected_mode(session, FALSE);
rtp_session_enable_adaptive_jitter_compensation(session, TRUE);
rtp_session_set_jitter_compensation(session, jitter); /* ms */
2014-03-24 10:41:04 +01:00
rtp_session_set_time_jump_limit(session, jitter * 16); /* ms */
2009-09-11 17:40:27 +02:00
if (rtp_session_set_payload_type(session, 0) != 0)
abort();
if (rtp_session_signal_connect(session, "timestamp_jump",
timestamp_jump, 0) != 0)
2009-09-11 17:40:27 +02:00
{
abort();
2009-09-05 00:04:44 +02:00
}
/*
* 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);
2009-09-11 17:40:27 +02:00
return session;
2009-09-05 00:04:44 +02:00
}
2009-09-11 17:40:27 +02:00
static int play_one_frame(void *packet,
size_t len,
2012-07-21 17:14:13 +02:00
OpusDecoder *decoder,
2009-09-11 17:40:27 +02:00
snd_pcm_t *snd,
const unsigned int channels)
2009-09-05 00:04:44 +02:00
{
int r;
2014-05-30 12:18:31 +02:00
int16_t *pcm;
snd_pcm_sframes_t f, samples = 1920;
2009-09-05 00:04:44 +02:00
2014-05-30 12:18:31 +02:00
pcm = alloca(sizeof(*pcm) * samples * channels);
2009-09-05 00:04:44 +02:00
2009-09-11 17:40:27 +02:00
if (packet == NULL) {
2014-05-30 12:18:31 +02:00
r = opus_decode(decoder, NULL, 0, pcm, samples, 1);
2009-09-05 00:04:44 +02:00
} else {
2014-05-30 12:18:31 +02:00
r = opus_decode(decoder, packet, len, pcm, samples, 0);
2009-09-05 00:04:44 +02:00
}
2012-07-21 17:14:13 +02:00
if (r < 0) {
fprintf(stderr, "opus_decode: %s\n", opus_strerror(r));
2009-09-05 00:04:44 +02:00
return -1;
}
2012-07-21 17:14:13 +02:00
f = snd_pcm_writei(snd, pcm, r);
2009-09-05 00:04:44 +02:00
if (f < 0) {
2014-03-23 19:49:21 +01:00
f = snd_pcm_recover(snd, f, 0);
if (f < 0) {
aerror("snd_pcm_writei", f);
return -1;
}
return 0;
2009-09-05 00:04:44 +02:00
}
2012-07-21 17:14:13 +02:00
if (f < r)
2009-09-05 00:04:44 +02:00
fprintf(stderr, "Short write %ld\n", f);
2012-07-21 17:14:13 +02:00
return r;
2009-09-05 00:04:44 +02:00
}
2009-09-11 17:40:27 +02:00
static int run_rx(RtpSession *session,
2012-07-21 17:14:13 +02:00
OpusDecoder *decoder,
2009-09-11 17:40:27 +02:00
snd_pcm_t *snd,
2012-07-28 21:44:19 +02:00
const unsigned int channels,
const unsigned int rate)
2009-09-05 00:04:44 +02:00
{
2009-09-11 17:40:27 +02:00
int ts = 0;
2009-09-05 00:04:44 +02:00
for (;;) {
2009-09-11 17:40:27 +02:00
int r, have_more;
char buf[32768];
void *packet;
r = rtp_session_recv_with_ts(session, (uint8_t*)buf,
2009-09-11 17:40:27 +02:00
sizeof(buf), ts, &have_more);
assert(r >= 0);
assert(have_more == 0);
if (r == 0) {
packet = NULL;
if (verbose > 1)
fputc('#', stderr);
} else {
packet = buf;
if (verbose > 1)
fputc('.', stderr);
2009-09-05 00:04:44 +02:00
}
r = play_one_frame(packet, r, decoder, snd, channels);
2009-09-11 17:40:27 +02:00
if (r == -1)
2009-09-05 00:04:44 +02:00
return -1;
/* Follow the RFC, payload 0 has 8kHz reference rate */
2012-07-28 21:44:19 +02:00
ts += r * 8000 / rate;
2009-09-11 17:40:27 +02:00
}
2009-09-05 00:04:44 +02:00
}
static void usage(FILE *fd)
{
2012-07-27 13:46:03 +02:00
fprintf(fd, "Usage: rx [<parameters>]\n"
"Real-time audio receiver over IP\n");
2009-09-05 00:04:44 +02:00
fprintf(fd, "\nAudio device (ALSA) parameters:\n");
2009-09-11 17:40:27 +02:00
fprintf(fd, " -d <dev> Device name (default '%s')\n",
2009-09-05 00:04:44 +02:00
DEFAULT_DEVICE);
2012-07-27 13:46:03 +02:00
fprintf(fd, " -m <ms> Buffer time (default %d milliseconds)\n",
2009-09-05 00:04:44 +02:00
DEFAULT_BUFFER);
fprintf(fd, "\nNetwork parameters:\n");
2009-09-11 17:40:27 +02:00
fprintf(fd, " -h <addr> IP address to listen on (default %s)\n",
DEFAULT_ADDR);
fprintf(fd, " -p <port> UDP port number (default %d)\n",
DEFAULT_PORT);
2012-07-27 13:46:03 +02:00
fprintf(fd, " -j <ms> Jitter buffer (default %d milliseconds)\n",
2009-09-11 17:40:27 +02:00
DEFAULT_JITTER);
fprintf(fd, "\nEncoding parameters (must match sender):\n");
2012-07-27 13:46:03 +02:00
fprintf(fd, " -r <rate> Sample rate (default %dHz)\n",
2009-09-11 17:40:27 +02:00
DEFAULT_RATE);
fprintf(fd, " -c <n> Number of channels (default %d)\n",
DEFAULT_CHANNELS);
2014-10-22 22:21:14 +02:00
fprintf(fd, "\nProgram parameters:\n");
2009-09-11 17:40:27 +02:00
fprintf(fd, " -v <n> Verbosity level (default %d)\n",
DEFAULT_VERBOSE);
2014-10-22 22:21:14 +02:00
fprintf(fd, " -D <file> Run as a daemon, writing process ID to the given file\n");
2009-09-05 00:04:44 +02:00
}
int main(int argc, char *argv[])
{
2012-07-21 17:14:13 +02:00
int r, error;
2009-09-05 00:04:44 +02:00
snd_pcm_t *snd;
2012-07-21 17:14:13 +02:00
OpusDecoder *decoder;
2009-09-11 17:40:27 +02:00
RtpSession *session;
2009-09-05 00:04:44 +02:00
/* command-line options */
const char *device = DEFAULT_DEVICE,
2014-10-22 22:21:14 +02:00
*addr = DEFAULT_ADDR,
*pid = NULL;
2009-09-11 17:40:27 +02:00
unsigned int buffer = DEFAULT_BUFFER,
rate = DEFAULT_RATE,
jitter = DEFAULT_JITTER,
channels = DEFAULT_CHANNELS,
port = DEFAULT_PORT;
2009-09-05 00:04:44 +02:00
2012-07-27 13:46:03 +02:00
fputs(COPYRIGHT "\n", stderr);
2012-07-21 14:32:40 +02:00
2009-09-05 00:04:44 +02:00
for (;;) {
int c;
2012-07-29 19:12:42 +02:00
c = getopt(argc, argv, "c:d:h:j:m:p:r:v:");
2009-09-05 00:04:44 +02:00
if (c == -1)
break;
switch (c) {
2012-07-21 17:40:18 +02:00
case 'c':
channels = atoi(optarg);
break;
2009-09-05 00:04:44 +02:00
case 'd':
device = optarg;
break;
2009-09-11 17:40:27 +02:00
case 'h':
addr = optarg;
break;
case 'j':
jitter = atoi(optarg);
break;
2009-09-05 00:04:44 +02:00
case 'm':
buffer = atoi(optarg);
break;
case 'p':
2009-09-11 17:40:27 +02:00
port = atoi(optarg);
break;
2012-07-29 19:12:42 +02:00
case 'r':
rate = atoi(optarg);
break;
2009-09-11 17:40:27 +02:00
case 'v':
verbose = atoi(optarg);
2009-09-05 00:04:44 +02:00
break;
2014-10-22 22:21:14 +02:00
case 'D':
pid = optarg;
break;
2009-09-05 00:04:44 +02:00
default:
usage(stderr);
return -1;
}
}
2012-07-21 17:14:13 +02:00
decoder = opus_decoder_create(rate, channels, &error);
2009-09-05 00:04:44 +02:00
if (decoder == NULL) {
2012-07-21 17:14:13 +02:00
fprintf(stderr, "opus_decoder_create: %s\n",
opus_strerror(error));
2009-09-05 00:04:44 +02:00
return -1;
}
2009-09-11 17:40:27 +02:00
ortp_init();
ortp_scheduler_init();
session = create_rtp_recv(addr, port, jitter);
assert(session != NULL);
2009-09-05 00:04:44 +02:00
r = snd_pcm_open(&snd, device, SND_PCM_STREAM_PLAYBACK, 0);
if (r < 0) {
aerror("snd_pcm_open", r);
return -1;
}
2009-09-11 17:40:27 +02:00
if (set_alsa_hw(snd, rate, channels, buffer * 1000) == -1)
2009-09-05 00:04:44 +02:00
return -1;
if (set_alsa_sw(snd) == -1)
return -1;
2014-10-22 22:21:14 +02:00
if (pid)
go_daemon(pid);
go_realtime();
2012-07-28 21:44:19 +02:00
r = run_rx(session, decoder, snd, channels, rate);
2009-09-05 00:04:44 +02:00
if (snd_pcm_close(snd) < 0)
abort();
2009-09-11 17:40:27 +02:00
rtp_session_destroy(session);
ortp_exit();
ortp_global_stats_display();
2012-07-21 17:14:13 +02:00
opus_decoder_destroy(decoder);
2009-09-05 00:04:44 +02:00
return r;
}