From 9c61e004ea28ca2ac142751576a51d86d16dec18 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 13 May 2018 01:23:18 +0200 Subject: [PATCH 1/2] Fix the -T option for mtx. --- mtx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mtx.c b/mtx.c index 960c5af..4231fde 100644 --- a/mtx.c +++ b/mtx.c @@ -57,7 +57,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Copyright (C) 2014-2017 Vittorio Gambaletta \n\n"); while (1) { - int c = getopt(argc, argv, "h:p:d:f:r:c:t:k:b:v:"); + int c = getopt(argc, argv, "h:p:d:f:r:c:t:k:b:v:T:"); if (c == -1) { break; } else if (c == 'h') { From 7c830057143d6285d5a7f7fe59c0259b15a7b997 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 13 May 2018 01:39:57 +0200 Subject: [PATCH 2/2] Support RTP output from mtx (-R 1). This isn't readable by mrx, but it is more compatible with other tools, e.g. ffmpeg: ffplay -protocol_whitelist rtp,file,udp -i mtx.sdp where output.sdp is the SDP file written to standard output --- README.md | 12 ++++++++++ mtrx.h | 9 +++++++ mtx.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 82 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c632e75..a745003 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Usage: mtx [] -h IP address (default: 239.48.48.1) -p UDP port (default: 1350) -d ALSA device name, or '-' for stdin (default: 'default') + -R RTP output (default: 0) -f Use float samples (1) or signed 16 bit integer samples (0) (default: 0) -r Audio sample rate (default: 48000 Hz) -c Audio channel count (default: 2) @@ -70,6 +71,17 @@ pcm.pnm { - On OpenWrt and/or with cheap USB audio cards without PulseAudio, if it doesn't work try **`mrx -d plughw:0,0`** - It shouldn't be needed anymore, but it might still be useful, so [this is a working `/etc/asound.conf` file for OpenWrt with cheap USB audio cards](https://gist.github.com/VittGam/ad0c1ce0143e4fb7a55fe8947b085e26) +### RTP + +RTP output is useful for transmitting to other applications, e.g. FFmpeg. +You don't need any PulseAudio integration; just run mtx with -R 1 and +-d etc. as usual. This will output an SDP file, which you can run in e.g. +ffplay with + +``` +ffplay -protocol_whitelist rtp,file,udp -i mtx.sdp +``` + ## Bugs - Well, all the desync bugs seem to happen (and needed a resync hack in `mtx`) only when using `alsa-pulse` to capture from the null output sink monitor... diff --git a/mtrx.h b/mtrx.h index 2fe6dce..3995100 100644 --- a/mtrx.h +++ b/mtrx.h @@ -41,6 +41,15 @@ struct __attribute__((__packed__)) azzp { unsigned char data; }; +struct __attribute__((__packed__)) rtp { + uint8_t version_p_x_cc; + uint8_t marker_and_pt; + uint16_t seq; + uint32_t timestamp; + uint32_t ssrc; + unsigned char data; +}; + struct __attribute__((__packed__)) timep { int64_t tv_sec; uint32_t tv_nsec; diff --git a/mtx.c b/mtx.c index 4231fde..39a0154 100644 --- a/mtx.c +++ b/mtx.c @@ -19,6 +19,7 @@ #include "mtrx.h" static unsigned long int kbps = 128; +static unsigned long int use_rtp = 0; static void *time_sync_thread(void *arg) { printverbose("Time sync thread started\n"); @@ -57,7 +58,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Copyright (C) 2014-2017 Vittorio Gambaletta \n\n"); while (1) { - int c = getopt(argc, argv, "h:p:d:f:r:c:t:k:b:v:T:"); + int c = getopt(argc, argv, "h:p:d:f:r:c:t:k:b:v:T:R:"); if (c == -1) { break; } else if (c == 'h') { @@ -66,6 +67,8 @@ int main(int argc, char *argv[]) { port = strtoul(optarg, NULL, 10); } else if (c == 'd') { device = optarg; + } else if (c == 'R') { + use_rtp = strtoul(optarg, NULL, 10); } else if (c == 'f') { use_float = strtoul(optarg, NULL, 10); } else if (c == 'r') { @@ -87,6 +90,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, " -h IP address (default: %s)\n", addr); fprintf(stderr, " -p UDP port (default: %lu)\n", port); fprintf(stderr, " -d ALSA device name, or '-' for stdin (default: '%s')\n", device); + fprintf(stderr, " -R RTP output (default: %lu)\n", use_rtp); fprintf(stderr, " -f Use float samples (1) or signed 16 bit integer samples (0) (default: %lu)\n", use_float); fprintf(stderr, " -r Audio sample rate (default: %lu Hz)\n", rate); fprintf(stderr, " -c Audio channel count (default: %lu)\n", channels); @@ -100,6 +104,11 @@ int main(int argc, char *argv[]) { } } + if (use_rtp && enable_time_sync) { + fprintf(stderr, "Disabling time synchronization due to RTP output.\n"); + enable_time_sync = 0; + } + int sock = init_socket(0); set_realtime_prio(); @@ -139,13 +148,40 @@ int main(int argc, char *argv[]) { } void *pcm = alloca(pcm_size); - struct azzp *packet = alloca(bytes_per_frame + sizeof(struct timep)); + struct azzp *azzp_packet = NULL; + struct rtp *rtp_packet = NULL; + if (use_rtp) { + rtp_packet = (struct rtp *)alloca(bytes_per_frame + sizeof(struct rtp) - 1); + } else { + azzp_packet = (struct azzp *)alloca(bytes_per_frame + sizeof(struct azzp) - 1); + } struct timespec clock = {0, 0}; int resync = 1; drop_privs_if_needed(); + // Used for RTP only. + srand(time(NULL)); + uint16_t rtp_seq = rand(); + uint32_t ssrc = rand(); + uint32_t ts = 0; + + if (use_rtp) { + printf("SDP file for this stream:\n\n"); + printf("v=0\n"); + printf("o=- 0 0 IN IP4 127.0.0.1\n"); + printf("s=No Name\n"); + printf("c=IN IP4 %s\n", addr); + printf("t=0 0\n"); + printf("a=tool:mtx\n"); + printf("m=audio %ld RTP/AVP 96\n", port); + printf("b=AS:96\n"); + printf("a=rtpmap:96 opus/48000/%lu\n", channels); + printf("a=fmtp:96 sprop-stereo=%d\n", channels > 1); + printf("a=control:streamid=0\n\n"); + } + while (1) { printverbose("clock %ld.%09lu\n", clock.tv_sec, clock.tv_nsec); @@ -204,11 +240,13 @@ int main(int argc, char *argv[]) { } } + unsigned char *data = use_rtp ? &rtp_packet->data : &azzp_packet->data; + ssize_t z; if (use_float) { - z = opus_encode_float(encoder, pcm, samples, &packet->data, bytes_per_frame); + z = opus_encode_float(encoder, pcm, samples, data, bytes_per_frame); } else { - z = opus_encode(encoder, pcm, samples, &packet->data, bytes_per_frame); + z = opus_encode(encoder, pcm, samples, data, bytes_per_frame); } if (z < 0) { fprintf(stderr, "opus_encode: %s\n", opus_strerror(z)); @@ -236,12 +274,26 @@ int main(int argc, char *argv[]) { printverbose("resync %lld %d\n", (((1000000000LL * (now.tv_sec - clock.tv_sec)) + (now.tv_nsec - clock.tv_nsec))), resync); clock = now; - packet->tv_sec = htobe64(now.tv_sec); - packet->tv_nsec = htobe32(now.tv_nsec); + if (use_rtp) { + rtp_packet->version_p_x_cc = (2 << 6); // Version 2, the other fields are zero. + rtp_packet->marker_and_pt = 96; // First ID that's dynamically allocated. + rtp_packet->seq = htobe16(rtp_seq++); + rtp_packet->timestamp = htobe32(ts); + rtp_packet->ssrc = htobe32(ssrc); + ts += samples; - if (sendto(sock, packet, z + sizeof(struct timep), 0, (struct sockaddr *) &addrin, sizeof(addrin)) < 0) { - perror("sendto"); - exit(1); + if (sendto(sock, rtp_packet, z + sizeof(struct rtp) - 1, 0, (struct sockaddr *) &addrin, sizeof(addrin)) < 0) { + perror("sendto"); + exit(1); + } + } else { + azzp_packet->tv_sec = htobe64(now.tv_sec); + azzp_packet->tv_nsec = htobe32(now.tv_nsec); + + if (sendto(sock, azzp_packet, z + sizeof(struct azzp) - 1, 0, (struct sockaddr *) &addrin, sizeof(addrin)) < 0) { + perror("sendto"); + exit(1); + } } }