Add UDP audio output for Linux

This commit is contained in:
ars-ka0s 2023-01-19 00:33:28 -06:00
parent 74d6cc6bc2
commit 3b6a105b5a
2 changed files with 108 additions and 11 deletions

View File

@ -75,6 +75,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netdb.h>
#include <errno.h> #include <errno.h>
@ -131,7 +132,9 @@ static struct adev_s {
enum audio_in_type_e g_audio_in_type; enum audio_in_type_e g_audio_in_type;
enum audio_out_type_e g_audio_out_type; enum audio_out_type_e g_audio_out_type;
int udp_sock; /* UDP socket for receiving data */ int udp_in_sock; /* UDP socket for receiving data */
int udp_out_sock; /* UDP socket for sending data */
struct sockaddr_storage udp_dest_addr; /* Destination address for UDP socket sending */
} adev[MAX_ADEVS]; } adev[MAX_ADEVS];
@ -239,7 +242,7 @@ int audio_open (struct audio_s *pa)
#else #else
adev[a].oss_audio_device_fd = -1; adev[a].oss_audio_device_fd = -1;
#endif #endif
adev[a].udp_sock = -1; adev[a].udp_in_sock = -1;
} }
@ -413,7 +416,7 @@ int audio_open (struct audio_s *pa)
//int data_size = 0; //int data_size = 0;
//Create UDP Socket //Create UDP Socket
if ((adev[a].udp_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) { if ((adev[a].udp_in_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't create socket, errno %d\n", errno); dw_printf ("Couldn't create socket, errno %d\n", errno);
return -1; return -1;
@ -425,7 +428,7 @@ int audio_open (struct audio_s *pa)
si_me.sin_addr.s_addr = htonl(INADDR_ANY); si_me.sin_addr.s_addr = htonl(INADDR_ANY);
//Bind to the socket //Bind to the socket
if (bind(adev[a].udp_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) { if (bind(adev[a].udp_in_sock, (const struct sockaddr *) &si_me, sizeof(si_me))==-1) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't bind socket, errno %d\n", errno); dw_printf ("Couldn't bind socket, errno %d\n", errno);
return -1; return -1;
@ -454,7 +457,7 @@ int audio_open (struct audio_s *pa)
} }
/* /*
* Output device. Only "soundcard" and "stdout" are supported at this time. * Output device. Soundcard, stdout, and UDP are supported at this time.
*/ */
if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) { if (strcasecmp(pa->adev[a].adevice_out, "stdout") == 0 || strcmp(pa->adev[a].adevice_out, "-") == 0) {
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT; adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
@ -463,6 +466,16 @@ int audio_open (struct audio_s *pa)
dw_printf ("stdout must only be used with the -O option\n"); dw_printf ("stdout must only be used with the -O option\n");
return (-1); return (-1);
} }
} else if (strncasecmp(pa->adev[a].adevice_out, "udp:", 4) == 0) {
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SDR_UDP;
/* User must supply address and port */
if (strcasecmp(pa->adev[a].adevice_out,"udp:") == 0 ||
strlen(pa->adev[a].adevice_out) < 7 ||
strstr(pa->adev[a].adevice_out+5, ":") == 0) {
text_color_set (DW_COLOR_ERROR);
dw_printf ("Destination address and port must be supplied for UDP output\n");
return (-1);
}
} else { } else {
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD; adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
} }
@ -514,7 +527,63 @@ int audio_open (struct audio_s *pa)
audio_out_name); audio_out_name);
return (-1); return (-1);
} }
break;
#endif #endif
case AUDIO_OUT_TYPE_SDR_UDP:;
struct addrinfo ai_out;
struct addrinfo *ai_res;
char udp_outhost[256];
char *udp_outport;
int res;
// Initialize structure for addrinfo restrictions
memset((char *) &ai_out, 0, sizeof(ai_out));
ai_out.ai_socktype = SOCK_DGRAM;
ai_out.ai_protocol = IPPROTO_UDP;
// Separate out the host and port strings
strncpy(udp_outhost, pa->adev[a].adevice_out+4, 255);
udp_outhost[255] = 0;
udp_outport = strstr(udp_outhost,":");
*udp_outport++ = 0;
if (strlen(udp_outport) == 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("UDP output destination port must be supplied\n");
return -1;
}
// Get the sockaddr to represent the host/port provided
res = getaddrinfo(udp_outhost, udp_outport, &ai_out, &ai_res);
if (res != 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Error parsing/resolving UDP output address\n");
return -1;
}
// IPv4 and IPv6 structs are different sizes
if (ai_res->ai_family == AF_INET6) {
res = sizeof(struct sockaddr_in6);
} else {
res = sizeof(struct sockaddr_in);
}
//Create UDP Socket for the right address family
if ((adev[a].udp_out_sock=socket(ai_res->ai_family, SOCK_DGRAM, IPPROTO_UDP))==-1) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Couldn't create socket, errno %d\n", errno);
return -1;
}
// Save sockaddr needed later to send the data and set buffer size
memcpy(&adev[a].udp_dest_addr, ai_res->ai_addr, res);
adev[a].outbuf_size_in_bytes = SDR_UDP_BUF_MAXLEN;
freeaddrinfo(ai_res);
break;
} }
/* /*
* Finally allocate buffer for each direction. * Finally allocate buffer for each direction.
@ -1222,8 +1291,8 @@ int audio_get (int a)
while (adev[a].inbuf_next >= adev[a].inbuf_len) { while (adev[a].inbuf_next >= adev[a].inbuf_len) {
int res; int res;
assert (adev[a].udp_sock > 0); assert (adev[a].udp_in_sock > 0);
res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0); res = recv(adev[a].udp_in_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
if (res < 0) { if (res < 0) {
text_color_set(DW_COLOR_ERROR); text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't read from udp socket, res=%d", res); dw_printf ("Can't read from udp socket, res=%d", res);
@ -1350,12 +1419,13 @@ int audio_put (int a, int c)
int audio_flush (int a) int audio_flush (int a)
{ {
switch (adev[a].g_audio_out_type) {
case AUDIO_OUT_TYPE_STDOUT:;
int res; int res;
unsigned char *ptr; unsigned char *ptr;
int len; int len;
switch (adev[a].g_audio_out_type) {
case AUDIO_OUT_TYPE_STDOUT:;
ptr = adev[a].outbuf_ptr; ptr = adev[a].outbuf_ptr;
len = adev[a].outbuf_len; len = adev[a].outbuf_len;
@ -1544,6 +1614,32 @@ int audio_flush (int a)
adev[a].outbuf_len = 0; adev[a].outbuf_len = 0;
return (0); return (0);
#endif #endif
case AUDIO_OUT_TYPE_SDR_UDP:;
ptr = adev[a].outbuf_ptr;
len = adev[a].outbuf_len;
while (len > 0) {
assert (adev[a].udp_out_sock > 0);
res = sendto(adev[a].udp_out_sock, adev[a].outbuf_ptr, len, 0, (struct sockaddr *)&adev[a].udp_dest_addr, sizeof(struct sockaddr_storage));
if (res <= 0) {
text_color_set(DW_COLOR_INFO);
dw_printf ("\nError %d writing to UDP socket. Exiting.\n", errno);
exit (0);
}
ptr += res;
len -= res;
}
adev[a].outbuf_len = 0;
return 0;
} }
return (0); return (0);
} /* end audio_flush */ } /* end audio_flush */
@ -1589,7 +1685,7 @@ void audio_wait (int a)
{ {
audio_flush (a); audio_flush (a);
if (adev[a].g_audio_out_type == AUDIO_OUT_TYPE_STDOUT) { if (adev[a].g_audio_out_type != AUDIO_OUT_TYPE_SOUNDCARD) {
return; return;
} }

View File

@ -45,6 +45,7 @@ enum audio_in_type_e {
enum audio_out_type_e { enum audio_out_type_e {
AUDIO_OUT_TYPE_SOUNDCARD, AUDIO_OUT_TYPE_SOUNDCARD,
AUDIO_OUT_TYPE_SDR_UDP,
AUDIO_OUT_TYPE_STDOUT }; AUDIO_OUT_TYPE_STDOUT };
/* For option to try fixing frames with bad CRC. */ /* For option to try fixing frames with bad CRC. */