Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add UDP audio output for Linux
  • Loading branch information
ars-ka0s committed Sep 25, 2023
commit 3b6a105b5ac3a362479289bffc9840150937ba96
118 changes: 107 additions & 11 deletions src/audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>


Expand Down Expand Up @@ -131,7 +132,9 @@ static struct adev_s {
enum audio_in_type_e g_audio_in_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];

Expand Down Expand Up @@ -239,7 +242,7 @@ int audio_open (struct audio_s *pa)
#else
adev[a].oss_audio_device_fd = -1;
#endif
adev[a].udp_sock = -1;
adev[a].udp_in_sock = -1;
}


Expand Down Expand Up @@ -413,7 +416,7 @@ int audio_open (struct audio_s *pa)
//int data_size = 0;

//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);
dw_printf ("Couldn't create socket, errno %d\n", errno);
return -1;
Expand All @@ -425,7 +428,7 @@ int audio_open (struct audio_s *pa)
si_me.sin_addr.s_addr = htonl(INADDR_ANY);

//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);
dw_printf ("Couldn't bind socket, errno %d\n", errno);
return -1;
Expand Down Expand Up @@ -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) {
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_STDOUT;
Expand All @@ -463,6 +466,16 @@ int audio_open (struct audio_s *pa)
dw_printf ("stdout must only be used with the -O option\n");
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 {
adev[a].g_audio_out_type = AUDIO_OUT_TYPE_SOUNDCARD;
}
Expand Down Expand Up @@ -514,7 +527,63 @@ int audio_open (struct audio_s *pa)
audio_out_name);
return (-1);
}
break;
#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.
Expand Down Expand Up @@ -1222,8 +1291,8 @@ int audio_get (int a)
while (adev[a].inbuf_next >= adev[a].inbuf_len) {
int res;

assert (adev[a].udp_sock > 0);
res = recv(adev[a].udp_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
assert (adev[a].udp_in_sock > 0);
res = recv(adev[a].udp_in_sock, adev[a].inbuf_ptr, adev[a].inbuf_size_in_bytes, 0);
if (res < 0) {
text_color_set(DW_COLOR_ERROR);
dw_printf ("Can't read from udp socket, res=%d", res);
Expand Down Expand Up @@ -1350,11 +1419,12 @@ int audio_put (int a, int c)

int audio_flush (int a)
{
int res;
unsigned char *ptr;
int len;

switch (adev[a].g_audio_out_type) {
case AUDIO_OUT_TYPE_STDOUT:;
int res;
unsigned char *ptr;
int len;

ptr = adev[a].outbuf_ptr;
len = adev[a].outbuf_len;
Expand Down Expand Up @@ -1544,6 +1614,32 @@ int audio_flush (int a)
adev[a].outbuf_len = 0;
return (0);
#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);
} /* end audio_flush */
Expand Down Expand Up @@ -1589,7 +1685,7 @@ void audio_wait (int 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;
}

Expand Down
1 change: 1 addition & 0 deletions src/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum audio_in_type_e {

enum audio_out_type_e {
AUDIO_OUT_TYPE_SOUNDCARD,
AUDIO_OUT_TYPE_SDR_UDP,
AUDIO_OUT_TYPE_STDOUT };

/* For option to try fixing frames with bad CRC. */
Expand Down